Rich Push Notifications Integration for iOS

  1. In XCode, Go to File -> New -> Target -> Notification Service Extension

     

     

  2. Create Notification Service Extension. Name your extension as NotificationService (You may choose a different name which will be referred to as Notification Service Target (name) in the subsequent sections).

     

  3. Click Activate to finish.

  4. At the end of this step, three new files will be created under NotificationService directory named:

    1. NotificationService.h

    2. NotificationService.m

    3. info.plist

  5. Open this up and you’ll see a couple of delegate methods – one for receiving a notification request and one for handling the expiration of the service extension. We’ll be focusing on the first one, didReceiveRequestWithContentHandler.

  6. The code in this method should look like this:
     

    NotificationService.m

    - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; //categories [[UNUserNotificationCenter currentNotificationCenter] getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> * _Nonnull categories) { NSString * categoryIdentifier =self.bestAttemptContent.categoryIdentifier; NSDictionary* lc = request.content.userInfo[@"aps"]; if (categoryIdentifier && lc) { self.bestAttemptContent.categoryIdentifier = [NSString stringWithFormat:@"%@_%@", categoryIdentifier, lc[@"lc"]]; } NSMutableSet* set = [[categories objectsPassingTest:^BOOL(UNNotificationCategory * _Nonnull obj, BOOL * _Nonnull stop) { return [obj.identifier isEqualToString:self.bestAttemptContent.categoryIdentifier]; }] mutableCopy] ; if ([set count] == 0) { self.bestAttemptContent.categoryIdentifier = [NSString stringWithFormat:@"%@_%@", categoryIdentifier, @"en"]; } }]; NSString *urlString = request.content.userInfo[@"ios_apx_media"]; [self loadAttachmentForUrlString:urlString completionHandler:^(UNNotificationAttachment *attachment) { if (attachment) { self.bestAttemptContent.attachments = [NSArray arrayWithObject:attachment]; } self.contentHandler(self.bestAttemptContent); }]; } - (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.contentHandler(self.bestAttemptContent); } - (void)loadAttachmentForUrlString:(NSString *)urlString completionHandler:(void(^)(UNNotificationAttachment *))completionHandler { __block UNNotificationAttachment *attachment = nil; NSURL *attachmentURL = [NSURL URLWithString:urlString]; [[[NSURLSession sharedSession] downloadTaskWithURL:attachmentURL completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error != nil) { NSLog(@"%@", error.localizedDescription); } else { NSString *dir = NSTemporaryDirectory(); NSString *tempFile = [NSString stringWithFormat:@"%@%@%@", @"file://", dir, [attachmentURL lastPathComponent]]; NSURL *tmpUrl = [NSURL URLWithString:tempFile]; NSFileManager *fileManager = [NSFileManager defaultManager]; [fileManager moveItemAtURL:location toURL:tmpUrl error:&error]; NSError *attachmentError = nil; attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:tmpUrl options:nil error:&attachmentError]; if (attachmentError) { NSLog(@"%@", attachmentError.localizedDescription); } } completionHandler(attachment); }] resume]; }



    NotificationService.swift

    var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) UNUserNotificationCenter.current().getNotificationCategories { (categories) in if let categoryIdentifier = self.bestAttemptContent?.categoryIdentifier, let lc = request.content.userInfo["aps"] { self.bestAttemptContent?.categoryIdentifier = categoryIdentifier + "_" + ((lc as! NSDictionary)["lc"] as! String) let categoryExistArray = categories.filter { (category) -> Bool in category.identifier == self.bestAttemptContent?.categoryIdentifier } if categoryExistArray.isEmpty { self.bestAttemptContent?.categoryIdentifier = categoryIdentifier + "_en" } } if let urlString = request.content.userInfo["ios_apx_media"], let fileUrl = URL(string: urlString as? String ?? "") { // Download the attachment URLSession.shared.downloadTask(with: fileUrl ) { (location, response, error) in if let location = location { // Move temporary file to remove .tmp extension let tmpDirectory = NSTemporaryDirectory() let tmpFile = "file://".appending(tmpDirectory).appending(fileUrl.lastPathComponent) let tmpUrl = URL(string: tmpFile)! try! FileManager.default.moveItem(at: location, to: tmpUrl) // Add the attachment to the notification content if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpUrl) { self.bestAttemptContent?.attachments = [attachment] } } // Serve the notification content self.contentHandler!(self.bestAttemptContent!) }.resume() } else { self.contentHandler!(self.bestAttemptContent!) } } } override func 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. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } }



    Note: The push payload has a field "ios_apx_media" which contains the url of the media and the URLSession downloads the media to temporary storage and appends a .tmp file extension, which we need to remove so that the application can infer the file type and display it. So, to do this we need to move the file to the application’s local File Manager and that’s it – our media is ready to be attached to the notification content, and served to the user.

    This is all the code needed in your Xcode project to display rich notifications in your application.

    Now, you just need to make a few adjustments to the server that triggers Push Notifications to your application using our API.

  7. To send the rich pushes we can use Appoxee front end and pass the link as a key to the notification.

     

     

  8. Once notification is sent we will receive it like the example below 

     

As shown in the figure above, you can now send images, videos, audio, and GIFs in your iOS 10 Push Notifications! Simply change the URL to point to another media file and the extension should then be able to handle other media types. 

And of course, you could remove the media attachment and just send text as was the case before this update.