Beta feature: Adding images to push notifications

The introduction of iOS 10 allows push notifications to include an image or gif. To enable this feature, there are a few steps that must be done to complete the integration.

Rich push images are currently a beta feature, so talk to your CSM to get access. Once you are whitelisted, you'll have to make sure your app is configured properly for Rich push.

Configuring your App for Rich Push

Before you can add images to your push notifications, you should check with your development team to ensure your app has been properly configured to receive rich push. See our full setup instructions for more. 

The image will only be visible if your app is configured to support rich push, and only on devices with the minimum required OS version. Otherwise, users will see just the message text.

iOS limitations

  • Device must be on iOS 10+
  • Leanplum SDK 1.4.2
  • Complete the setup steps listed below.

Android limitations 

  • Device must be on Android 4.1+ (API Level 16)
  • Leanplum SDK 1.3.2
  • Android images require an aspect ratio of 2:1 (consider trying sizes 512x256, 960x480, or 1024x512). Note: iOS does not define a required aspect ratio.
  • We use the BigPicture mode for Android notifications. In this format, only one line of summary is shown when the message is expanded, regardless of the image size. This is enforced by Android and without having a custom layout for the push notification it is not possible to have a multiline summary with a big picture. The summary is intended to complement the image and should be very short while the main focus should be on the image.
  • With GIFs, Android will only display a static image in the notification.

Adding an Image to a Push Notification

We recommend thoroughly testing on both iOS and Android devices to ensure the image is rendering as expected (see Android limitations above). 

Be sure to write copy that makes sense without the image so that users on incompatible app versions will still understand the message. If you would like to send your message only to users who are rich push-compatible, reach out to your customer success manager and they can add a custom target for you.

  1. Create a new message and select Push Notification.
  2. Choose targets.
  3. Click the Image field to choose an image.
  4. Upload an image. If possible, we recommend uploading images directly to Leanplum. Alternatively, you can provide your own image URL. JPEG, PNG, and GIF files up to 10MBs are supported. URLs must use valid HTTPS. If providing an image URL, iOS requires URLs to support TLS 1.2.
  5. Preview the push to verify the image displays on device as expected.
  6. Send the message.
    Screen_Shot_2016-11-30_at_5.49.03_PM.png

 

 

iOS setup

1. Create a Notification Service Extension

Within your project, you must create a Service Extension. A Service Extension allows for the modification of a notification to include rich media. To add a notification service extension, click on File -> New -> Target and select Notification Service Extension

2. Setting up Notification Service Extension

Since the Notification Service Extension has it's own bundle id, it must be set up with it's own App ID and provisioning profile. Please verify through the Apple Developer Service. 

3. Add the following code to Notification Service Extension

While you are free to implement your own method within the Notification Service Extension, you will need to make sure that your code is correctly handling the media that is being sent with Leanplum. The key that is associated with the rich media in the Leanplum Payload is: LP_URL. 

@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];
    NSDictionary *userInfo = request.content.userInfo;
    
    // LP_URL is the key that is used from Leanplum to
    // send the image URL in the payload.
    //
    // If there is no LP_URL in the payload than
    // the code will still show the push notification.
    if (userInfo == nil || userInfo[@"LP_URL"] == nil) {
        self.contentHandler(self.bestAttemptContent);
        return;
    }
    
    NSString *attachmentMedia = userInfo[@"LP_URL"];
    
    // If there is an image in the payload, this part
    // will handle the downloading and displaying of the image.
    if (attachmentMedia) {
        NSURL *URL = [NSURL URLWithString:attachmentMedia];
        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];
            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";
    }
}
    
- (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);
}

@end
class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        let imageKey = "LP_URL"
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        // MARK: - Leanplum Rich Push
        if let bestAttemptContent = bestAttemptContent {
            let userInfo = request.content.userInfo;
            
            // LP_URL is the key that is used from Leanplum to
            // send the image URL in the payload.
            //
            // If there is no LP_URL in the payload than
            // the code will still show the push notification.
            if userInfo[imageKey] == nil {
                contentHandler(bestAttemptContent);
                return;
            }
            
            // If there is an image in the payload,
            // download and display the image.
            if let attachmentMedia = userInfo[imageKey] as? String {
                let mediaUrl = URL(string: attachmentMedia)
                let LPSession = URLSession(configuration: .default)
                LPSession.downloadTask(with: mediaUrl!, completionHandler: { temporaryLocation, response, error in
                    if let err = error {
                        print("Leanplum: Error with downloading rich push: \(String(describing: err.localizedDescription))")
                        contentHandler(bestAttemptContent);
                        return;
                    }
                    
                    let fileType = self.determineType(fileType: (response?.mimeType)!)
                    let fileName = temporaryLocation?.lastPathComponent.appending(fileType)
                    
                    let temporaryDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName!)
                    
                    do {
                        try FileManager.default.moveItem(at: temporaryLocation!, to: temporaryDirectory)
                        let attachment = try UNNotificationAttachment(identifier: "", url: temporaryDirectory, options: nil)
                        
                        bestAttemptContent.attachments = [attachment];
                        contentHandler(bestAttemptContent);
                        // The file should be removed automatically from temp
                        // Delete it manually if it is not
                        if FileManager.default.fileExists(atPath: temporaryDirectory.path) {
                            try FileManager.default.removeItem(at: temporaryDirectory)
                        }
                    } catch {
                        print("Leanplum: Error with the rich push attachment: \(error)")
                        contentHandler(bestAttemptContent);
                        return;
                    }
                }).resume()
                
            }
        }
    }
    
    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)
        }
    }
    
    // MARK: - Leanplum Rich Push
    func determineType(fileType: String) -> String {
        // Determines the file type of the attachment to append to URL.
        if fileType == "image/jpeg" {
            return ".jpg";
        }
        if fileType == "image/gif" {
            return ".gif";
        }
        if fileType == "image/png" {
            return ".png";
        } else {
            return ".tmp";
        }
    }

}

4. Update didReceiveRemoteNotification

Please make sure to update the application instance method didReceiveRemoteNotification, so that it does execute the download of the rich media. See below for a simple example.

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {    
    completionHandler(UIBackgroundFetchResultNewData);
}
func application(_ application: UIApplication,
                         didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                         fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        completionHandler(.newData)
}

5. Create a Rich Push from our Dashboard

After implementing the code above, you are free to create a rich push notification from our dashboard. 

 


Was this article helpful?
Have more questions? Submit a request