丰富的推送通知不适用于 IOS 中的 FCM

Posted

技术标签:

【中文标题】丰富的推送通知不适用于 IOS 中的 FCM【英文标题】:Rich push notification not working with FCM in IOS 【发布时间】:2018-07-18 12:29:42 【问题描述】:

我在我的项目中添加了UNNotificationServiceExtensionUNNotificationContentExtension 用于丰富的推送通知。请参考下面我添加的相同代码。

代码:

#import "NotificationService.h"

@interface NotificationService ()

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

@end

@implementation NotificationService 
    NSURLSession *session;


- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler 

    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    NSDictionary *userInfo = request.content.userInfo;
    if (userInfo == nil) 
        [self contentComplete];
        return;
    

    if ([userInfo objectForKey:@"pic_url"]) 
        [self loadAttachmentForUrlString:[userInfo objectForKey:@"pic_url"]
                       completionHandler: ^(UNNotificationAttachment *attachment) 
                           self.bestAttemptContent.attachments = [NSArray arrayWithObjects:attachment, nil];
                       ];
    


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

    __block UNNotificationAttachment *attachment = nil;
    __block NSURL *attachmentURL = [NSURL URLWithString:urlString];

    NSString *fileExt = [@"." stringByAppendingString:[urlString pathExtension]];


    session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

    NSURLSessionDownloadTask *task = [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:[attachmentURL lastPathComponent]
                                                                                                                    URL:localURL
                                                                                                                options:nil
                                                                                                                  error:&attachmentError];
                                                        if (attachmentError)
                                                        
                                                            NSLog(@"%@", attachmentError.localizedDescription);
                                                        
                                                    
                                                    completionHandler(attachment);
                                                ];

    [task resume];


- (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

    [session invalidateAndCancel];
    self.contentHandler(self.bestAttemptContent);


@end

我正在使用以下有效负载

    
   "to": "9yJUWBA",
   "mutable_content": true,
   "category": "myNotificationCategory",
   "notification":
                                                  
      "title":"Realtime Custom Push Notifications",
       "subtitle":"Now with ios 10 support!",
       "body":"Add multimedia content to your notifications"
   

问题是我没有收到通知。我使用以下教程来实现丰富的推送通知。我检查了可用的不同答案,但没有一个对我有用。我还尝试通过附加扩展进程来调试 didReceiveNotificationRequest 方法,但没有触发断点。

https://mobisoftinfotech.com/resources/mguide/ios-10-rich-notifications-tutorial/

【问题讨论】:

一旦某些键丢失,例如“aps”、“Alert”等,您是否会检查有效负载... 试试这个解决方案***.com/a/50523908/5084797 【参考方案1】:
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler

    //Called when a notification is delivered to a foreground app.
    NSLog(@"Userinfo willPresentNotification%@",notification.request.content.userInfo);

    // Print message ID.
    /*
     NSDictionary *userInfo = notification.request.content.userInfo;
     if (userInfo[kGCMMessageIDKey]) 
     NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
     
     */


    completionHandler(UNNotificationPresentationOptionAlert);



-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler

    //Called to let your app know which action was selected by the user for a given notification.
    NSLog(@"Userinfo didReceiveNotificationResponse%@",response.notification.request.content.userInfo);

    // Print message ID.
    /*
     NSDictionary *userInfo = notification.request.content.userInfo;
     if (userInfo[kGCMMessageIDKey]) 
     NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
     
     */



使用上述方法,这些方法适用于 iOS 10 及更高版本。 如需调试通知服务扩展,请访问以下链接

Debug Notification Extensions

如果您从 iOS 9 开始支持您的应用程序,那么您必须将以下两种方法都用于 iOS 10 以上,并且您还在问题中写道

- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo


    // If you are receiving a notification message while your app is in the background,
    // this callback will not be fired till the user taps on the notification launching the application.
    // TODO: Handle data of notification

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
    /*
     // Print message ID.
     if (userInfo[kGCMMessageIDKey]) 
     NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
     
     */
    // Print full message.
    NSLog(@"Userinfo didReceiveRemoteNotification 1 %@",userInfo);




- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
    // If you are receiving a notification message while your app is in the background,
    // this callback will not be fired till the user taps on the notification launching the application.
    // TODO: Handle data of notification

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

    /*
     // Print message ID.
     if (userInfo[kGCMMessageIDKey]) 
     NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
     
     */
    // Print full message.

    NSLog(@"Userinfo didReceiveRemoteNotification 2 %@",userInfo);


    completionHandler(UIBackgroundFetchResultNewData);

如果您有任何疑问,请告诉我。

【讨论】:

【参考方案2】:
@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.title = @"";
    self.bestAttemptContent.subtitle = @"";
//    self.bestAttemptContent.body = [NSString stringWithFormat:@"%@", self.bestAttemptContent.body];

    NSDictionary *dictPushNotiData = request.content.userInfo;

    NSString *imageURL = @"";
    NSString *videoURL = @"";

    if(dictPushNotiData[@"xxx_details"])
    
        NSString *jsonString = [dictPushNotiData objectForKey:@"xxx_details"];
        NSData *jsonData =  [jsonString dataUsingEncoding:NSUTF8StringEncoding];

        // If Instagram Notification Informations then call InstagramViewController
        if(jsonData)
        
            NSMutableDictionary *dictXXX = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
            NSLog(@"dictXXX : %@",dictXXX);

            //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@", [dictXXX objectForKey:@"xxxx"]];
            //self.bestAttemptContent.subtitle = [NSString stringWithFormat:@"%@", [dictXXX objectForKey:@"xxxx"]];

            NSString  *strBody = @"Notification xxxx x xxxx Post";

            if([[dictXXX objectForKey:@"xxxx"] length] > 0)
            
                strBody = [dictXXX objectForKey:@"xxxx"];
            

            self.bestAttemptContent.body = [NSString stringWithFormat:@"%@",strBody];

            imageURL = [dictXXX objectForKey:@"xxxx"];
            videoURL = [dictXXX objectForKey:@"xxxx"];
        
    

    NSString *strAttachment = nil;
    //    if (videoURL.length > 0)
    //     //Prioritize videos over image
    //        strAttachment = videoURL;
    //    
    //    else

    if (imageURL.length > 0)
    
        strAttachment = imageURL;
    
    else
    
        self.contentHandler(self.bestAttemptContent); //Nothing to add to the push, return early.
        return;
    

    // If there is an image in the payload, this part
    // will handle the downloading and displaying of the image.
    if (strAttachment) 
        NSURL *URL = [NSURL URLWithString:strAttachment];
        NSURLSession *LPSession = [NSURLSession sessionWithConfiguration:
                                   [NSURLSessionConfiguration defaultSessionConfiguration]];
        [[LPSession downloadTaskWithURL:URL completionHandler: ^(NSURL *temporaryLocation, NSURLResponse *response, NSError *error) 
            if (error) 
                NSLog(@"Leanplum: Error with downloading rich push: %@",[error localizedDescription]);
                self.contentHandler(self.bestAttemptContent);
                return;
            

            NSString *fileType = [self determineType:[response MIMEType]];
            NSString *fileName = [[temporaryLocation.path lastPathComponent] stringByAppendingString:fileType];
            NSString *temporaryDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
            [[NSFileManager defaultManager] moveItemAtPath:temporaryLocation.path toPath:temporaryDirectory error:&error];

            NSError *attachmentError = nil;
            UNNotificationAttachment *attachment =
            [UNNotificationAttachment attachmentWithIdentifier:@"" URL:[NSURL fileURLWithPath:temporaryDirectory] options:nil error:&attachmentError];

            if (attachmentError != NULL) 
                NSLog(@"Leanplum: Error with the rich push attachment: %@",
                      [attachmentError localizedDescription]);
                self.contentHandler(self.bestAttemptContent);
                return;
            
            self.bestAttemptContent.attachments = @[attachment];

            NSLog(@"self.bestAttemptContent.attachments : %@",
                  self.bestAttemptContent.attachments);

            self.contentHandler(self.bestAttemptContent);
            [[NSFileManager defaultManager] removeItemAtPath:temporaryDirectory error:&error];
        ] resume];
    


- (NSString*)determineType:(NSString *) fileType 
    // Determines the file type of the attachment to append to NSURL.
    if ([fileType isEqualToString:@"image/jpeg"])
        return @".jpg";
    
    if ([fileType isEqualToString:@"image/gif"]) 
        return @".gif";
    
    if ([fileType isEqualToString:@"image/png"]) 
        return @".png";
     else 
        return @".tmp";
    

【讨论】:

以上是关于丰富的推送通知不适用于 IOS 中的 FCM的主要内容,如果未能解决你的问题,请参考以下文章

推送通知不适用于 FCM

FCM - 推送通知仅适用于控制台而不适用于邮递员

iOS Swift 使用 Firebase 云消息发送丰富的推送通知

iOS VoIP 推送通知/PushKit 不适用于增强通知格式

iOS 推送通知中的多行标题

推送通知不适用于 iPhone 所有设备 (Xcode8) 版本