Objective-C 中的 iOS 10 富媒体推送通知(媒体附件)

Posted

技术标签:

【中文标题】Objective-C 中的 iOS 10 富媒体推送通知(媒体附件)【英文标题】:iOS 10 Rich Media Push Notification (Media Attachment) in Objective-C 【发布时间】:2016-09-24 07:23:12 【问题描述】:

我想在 ios 10 推送通知中添加媒体,尤其是图像和视频,但图像在推送中不起作用。如何在 Objective-C 中做到这一点?我的代码如下:

AppDelegate.h

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <UserNotifications/UserNotifications.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate,CLLocationManagerDelegate,UNUserNotificationCenterDelegate>

@property (strong, nonatomic) UIWindow *window;
@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    if(SYSTEM_VERSION_LESS_THAN( @"10.0" )) 
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
     else
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate = self;
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error)
        
            if( !error ) 
                [[UIApplication sharedApplication] registerForRemoteNotifications];
                // required to get the app to do anything at all about push notifications
                NSLog( @"Push registration success." );
             else 
                NSLog( @"Push registration FAILED" );
                NSLog( @"ERROR: %@ - %@", error.localizedFailureReason, error.localizedDescription );
                NSLog( @"SUGGESTIONS: %@ - %@", error.localizedRecoveryOptions, error.localizedRecoverySuggestion );
            
        ];
    


-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 
    NSString * token = [NSString stringWithFormat:@"%@", deviceToken];
    //Format token as per need:
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    token = [token stringByReplacingOccurrencesOfString:@">" withString:@""];
    token = [token stringByReplacingOccurrencesOfString:@"<" withString:@""];
     NSLog(@"Device Token is \n%@",token);


-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error

    NSLog(@"Error:%@",error);


-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO( @"10.0" )) 
        [self application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result)];
     else 
        /// previous stuffs for iOS 9 and below. I've shown an alert wth received data.
    


-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResult))completionHandler 
    // iOS 10 will handle notifications through other methods

    if( NOTIFY_VISITORS_SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO( @"10.0" ) )
    
        NSLog( @"iOS version >= 10. Let NotificationCenter handle this one." );
        // set a member variable to tell the new delegate that this is background
        return;
    
    NSLog( @"HANDLE PUSH, didReceiveRemoteNotification: %@", userInfo );
    // custom code to handle notification content
    if( [UIApplication sharedApplication].applicationState == UIApplicationStateInactive )
    
        NSLog( @"INACTIVE" );
        completionHandler( UIBackgroundFetchResultNewData );
    
    else if( [UIApplication sharedApplication].applicationState == UIApplicationStateBackground )
      
        NSLog( @"BACKGROUND" );  
        completionHandler( UIBackgroundFetchResultNewData );  
      
    else  
      
        NSLog( @"FOREGROUND" );  
        completionHandler( UIBackgroundFetchResultNewData );  
      


- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler 
    NSLog( @"Handle push from foreground" );
    // custom code to handle push while app is in the foreground
    NSLog(@"%@", notification.request.content.userInfo);


- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler 
    NSLog( @"Handle push from background or closed" );
    // if you set a member variable in didReceiveRemoteNotification, you  will know if this is from closed or background
    NSLog(@"%@", response.notification.request.content.userInfo);

然后我添加了一个新的目标通知服务扩展如下:

NotificationService.h

#import <UserNotifications/UserNotifications.h>
@interface NotificationService : UNNotificationServiceExtension
@end

NotificationService.m

#import "NotificationService.h"

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler 
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

    // Modify the notification content here...
    //self.bestAttemptContent.body = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.body];

    // check for media attachment, example here uses custom payload keys mediaUrl and mediaType
    NSDictionary *userInfo = request.content.userInfo;
    if (userInfo == nil) 
        [self contentComplete];
        return;
    

    NSString *mediaUrl = userInfo[@"mediaUrl"];
    NSString *mediaType = userInfo[@"mediaType"];

    if (mediaUrl == nil || mediaType == nil) 
        [self contentComplete];
        return;
    

    // load the attachment
    [self loadAttachmentForUrlString:mediaUrl
                            withType:mediaType
                   completionHandler:^(UNNotificationAttachment *attachment) 
                       if (attachment) 
                           self.bestAttemptContent.attachments = [NSArray arrayWithObject:attachment];
                       
                       [self contentComplete];
                   ];



- (void)serviceExtensionTimeWillExpire 
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    [self contentComplete];


- (void)contentComplete 
    self.contentHandler(self.bestAttemptContent);


- (NSString *)fileExtensionForMediaType:(NSString *)type 
    NSString *ext = type;

    if ([type isEqualToString:@"image"]) 
        ext = @"jpg";
    

    if ([type isEqualToString:@"video"]) 
        ext = @"mp4";
    

    if ([type isEqualToString:@"audio"]) 
        ext = @"mp3";
    

    return [@"." stringByAppendingString:ext];


- (void)loadAttachmentForUrlString:(NSString *)urlString withType:(NSString *)type completionHandler:(void(^)(UNNotificationAttachment *))completionHandler  

    __block UNNotificationAttachment *attachment = nil;
    NSURL *attachmentURL = [NSURL URLWithString:urlString];
    NSString *fileExt = [self fileExtensionForMediaType:type];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    [[session downloadTaskWithURL:attachmentURL
                completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) 
                    if (error != nil) 
                        NSLog(@"%@", error.localizedDescription);
                     else 
                        NSFileManager *fileManager = [NSFileManager defaultManager];
                        NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];
                        [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

                        NSError *attachmentError = nil;
                        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];
                        if (attachmentError) 
                        NSLog(@"%@", attachmentError.localizedDescription);
                        
                    
                    completionHandler(attachment);
                ] resume];

@end

我的通知服务扩展 Info.plist 是:

我正在使用 php 脚本发送推送通知,如下所示:

TestPush.php

<?php

// Put your device token here (without spaces):
  $deviceToken = 'my device tocken goes here';
// Put your private key's passphrase here:
$passphrase = 'mypassphase';
// Put your alert message here:
$message = 'Test iOS 10 Media Attachment Push';

////////////////////////////////////////////////////////////////////////////////

$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');

// Open a connection to the APNS server
$fp = stream_socket_client(
    'ssl://gateway.sandbox.push.apple.com:2195', $err,
    $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

if (!$fp)
    exit("Failed to connect: $err $errstr" . PHP_EOL);

echo 'Connected to APNS' . PHP_EOL;

// Create the payload body
$body['aps'] = array(
    'alert' => $message,
    'sound' => 'default',
     'mutable-content' => 1,
     'category'=> "pusher"
    );
$body['data'] = array(
    'mediaUrl' => "http://www.alphansotech.com/wp-content/uploads/2015/12/Push-notification-1.jpg",
    'mediaType' => "jpg"
);
// Encode the payload as JSON
$payload = json_encode($body);

// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;

// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));

if (!$result)
    echo 'Message not delivered' . PHP_EOL;
else
    echo 'Message successfully delivered' . PHP_EOL;

// Close the connection to the server
fclose($fp);

共享图像和文件托管在服务器上,推送将发送其链接以显示它。

谁能帮帮我。

【问题讨论】:

您好,我也在尝试在 Objective-C 上实现此功能,但在网上找不到任何资源...您能否分享您的代码和/或指向您的资源的链接用过的?谢谢! 上面的代码工作正常,只有音频和视频不能正常工作。只需更改有效负载格式,请参阅this question 的第一个答案。如果您找到任何支持音频和视频的解决方案,请也参考我该解决方案。 我已经实现了上面的代码和有效负载,但它没有完全工作,我认为它与 info.plist 有关,因为我在你的屏幕截图中看不到键名。 NotificationServiceExtension部分的info.plist,源码为:&lt;key&gt;NSExtension&lt;/key&gt; &lt;dict&gt;&lt;key&gt;NSExtensionAttributes&lt;/key&gt; &lt;dict&gt; &lt;key&gt;UNNotificationExtensionCategory&lt;/key&gt; &lt;string&gt;myCat&lt;/string&gt; &lt;key&gt;UNNotificationExtensionInitialContentSizeRatio&lt;/key&gt; &lt;real&gt;0.7&lt;/real&gt; &lt;key&gt;UNNotificationExtensionDefaultContentHidden&lt;/key&gt; &lt;true/&gt; &lt;/dict&gt; &lt;key&gt;NSExtensionPointIdentifier&lt;/key&gt; &lt;string&gt;com.apple.usernotifications.service&lt;/string&gt; &lt;key&gt;NSExtensionPrincipalClass&lt;/key&gt; &lt;string&gt;NotificationService&lt;/string&gt; &lt;/dict&gt; 在这个 plist 中有一个键 UNNotificationExtensionCategory 它没有在我的代码中用于 ow 但我认为它是音频和视频支持所必需的。我正在尝试这样做,我当我得到解决方案时会更新你。如果你能解决问题,请更新我。谢谢!! 【参考方案1】:

不起作用的其他警告是拥有通知服务 Deployment Target您的测试设备不支持。

在我的例子中,通知服务模板自动将其Deployment Target 设置为10.2,而我的测试设备是10.1

我浪费了几个小时来配置我的扩展程序设置,而它一直在工作!

【讨论】:

也被这个抓住了。 这很有帮助。谢谢 超级兄弟。节省了一天。【参考方案2】:

您的代码没问题,只是需要不同的推送通知数据格式:

尝试更换这部分:

// Create the payload body
$body['aps'] = array(
    'alert' => $message,
    'sound' => 'default',
     'mutable-content' => 1,
     'category'=> "pusher"
    );
$body['data'] = array(
    'mediaUrl' => "http://www.alphansotech.com/wp-content/uploads/2015/12/Push-notification-1.jpg",
    'mediaType' => "jpg"
);

与:

$body = array(
  'aps' => array(
    'alert' => 'Rich notification',
    'sound' => 'default',
    'mutable-content' => 1
  ),
  'mediaUrl'  => 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/FloorGoban.JPG/1024px-FloorGoban.JPG',
  'mediaType' => 'image'
);

请注意,该图片应该可以通过https://访问

【讨论】:

它工作正常,但我在提供嵌入代码链接时无法播放 youtube 视频。我想更新应该支持所有图像、视频和音频格式的代码。 我想在推送中播放视频,但上面的代码不支持如何做到这一点,你能帮我从上面的代码中播放视频吗? @Ashraf 能否提供任何示例演示链接以了解丰富推送通知的使用和项目创建?? 查看推送通知中富媒体附件的演示see this link 完美答案。

以上是关于Objective-C 中的 iOS 10 富媒体推送通知(媒体附件)的主要内容,如果未能解决你的问题,请参考以下文章

适用于 iOS 应用的自管理富广告平台?

动态卡片:富媒体内容井喷式增长下,新一代移动端动态研发的模式

Objective-c:在 ios 11 更新 swrevealcontroller 中的黑色状态栏后

无法使用 ios 10 中的通知服务扩展在远程通知中附加媒体

个推消息推送SDK之“富媒体样式”功能详解

IOS/Objective-C:检测自定义表格视图单元格中的按钮按下?