iOS 10 Rich Media Push Notification (Media Attachment) in Objective-C

14,024

Solution 1

your code is ok, it just expects a different push notification data format:

Try replacing this part:

// 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"
);

with:

$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'
);

Please note, that image should be accessible via https://

Solution 2

Additional caveat for this not to work is having the Notification Service Deployment Target value that's not supported by your test device.

In my case the Notification Service template had automatically set its Deployment Target to 10.2 while my test device is 10.1

I wasted hours configuring my extension setup while its already working all along!

Share:
14,024
Mohammad Ashraf Ali
Author by

Mohammad Ashraf Ali

Software engineer at NotifyVisitors with a passion for developing ios apps. Love to share what I learn with the rest of the developer community at StackOverflow.

Updated on June 12, 2022

Comments

  • Mohammad Ashraf Ali
    Mohammad Ashraf Ali almost 2 years

    I want to add Media especially images and videos in iOS 10 Push notification but images are not working in push. How to do that in Objective-C? My Code is as follows:

    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);
    

    Then I've added a new target Notification Service Extension as following:

    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
    

    and my Notification Service Extension Info.plist is: Notification Service Extension Info.plist

    And I'm using php Script to send push notification as follows:

    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);
    

    Sharing Images and file are hosted on server and push will send its link to show it.

    Can anyone help me please.

  • Mohammad Ashraf Ali
    Mohammad Ashraf Ali over 7 years
    It is working but I'm unable to play youtube video while I have given embed code link. I want to update my code that should support all image, video and audio format.
  • Mohammad Ashraf Ali
    Mohammad Ashraf Ali over 7 years
    I want to play video in push but it is not supporting in above code how to do that can you help me to play video from above code.
  • Ramakrishna
    Ramakrishna over 7 years
    @Ashraf can you provide any sample demo links to know about the usage and project creation for the rich push notifications??
  • Mohammad Ashraf Ali
    Mohammad Ashraf Ali over 7 years
    to see demo of Rich Media Attachment in Push notification see this link
  • nevan king
    nevan king about 7 years
    Caught by this too.
  • Mehul
    Mehul almost 7 years
    Perfect answer.
  • Swati
    Swati almost 5 years
    this helped a lot. Thanks
  • MRizwan33
    MRizwan33 over 2 years
    super bro. saved a day.