Welcome to Innominds Blog. As thought leaders and visionaries in the tech industry, our blog serves as your resource for innovative ideas, advanced technical solutions and industry-standard technologies. Enjoy our insights and engage with us!

Innominds Blog

User Interaction On Notifications Made Simple and Flexible With Rich Notifications In iOS

By Krishna Prakash Narasannagari,
Introduction

Notifications have become trendier with the new visual refresh in iOS 10. As part of a new framework, Apple has introduced new features that help us make notifications that contain images, sound and video.

The application enables us to provide an option for an instant reply for the notification without opening it.

All mobile marketers are using rich notifications to make user interactions on notifications simple and flexible. Today, 95% of push notifications comprise of media. This has led to 18% increase in the number of apps opened per month that are attributed to notifications.

BLOG_img_IOS_push

Supported Media

Each platform supports different types of media.

iOS

  • Image (JPEG, GIF, PNG) — Audio (AIFF, WAV, MP3, M4A)
  • Video (MPEG, MPEG2, MP4, AVI)

Android

  • Image (JPEG, PNG)

Size Limit

The theoretical maximum file sizes are 5MB for the image, 10MB for audio and 50MB for video.

Analytics

We can add analytics to measure user behavior on rich notifications and apps usage.

Example:

  1. Does the user open the rich notification?
  2. How frequently is the user opening the app by sending rich notifications?
  3. Are they interacting with the notifications like an Instant reply?

Mobile marketers can improve their business logic and strategy based on these analytics.

 
Using Rich Notifications

Pre-requirements

  1. Xcode 8.0+
  2. Minimum iOS 10
  3. OS X 10.11
  4. UserNotifications.framework

Rich Notification payload is like normal APNS payload, but with some extra parameters in the payload.

Sample Payload Format

{
  "aps": {
    "alert": {
          "body": "Push notification body",
          "title": "Push notification title"
      },
      "mutable-content": 1,                                               
      "category": "Category Name"
    },
     "attachment": "url to be displayed"
}

Note: This payload format will change depending upon the requirements

Significance

Parameter

Significance

content-available

Property that signifies that there are some extra keys to be taken into consideration, content-available: 1

mutable-content

This signifies that the OS should initiate the service extension of the app to perform some extra processing

category

This name must be equal with the category name in notification content extension info.plist

attachment

It gives the download link for the Image/Video/Gif to be attached


Bundle ID

If you are familiar with group IDs, you can use them. Or else, use 3 different bundle IDs.

Sample:
com.companyname.appname
com.companyname.appname.NotificationContent
com.companyname.appname.NotificationExtension

Create provisional profiles for the bundle IDs and enable push notifications.

Sample APP

Step 1: Initial Setup

  1. Create a sample project with the single view app
  2. Change the deployment target to iOS 10.0
  3. Enable push notifications in Project -> Targets -> Capabilities
    IOS Enable Push notifications in Project
  4. Enable background mode with remote notifications in Project -> Targets -> CapabilitiesEnable Background mode with Remote notifications in Project
  5. Import UserNotifications.framework and UserNotificationsUI.framework
Coding

Step 2: Getting Device Token

  1. Import UserNotifications in AppDelegate then add UNUserNotificationCenterDelegate protocol
  2. Call register for notifications for APNS token
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool  {
            // Override point for customization after application launch.
            UNUserNotificationCenter.current().delegate = self
            if #available(iOS 10, *) {
              UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ granted, error in }
            } else { // iOS 9 support
              application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
            }
            application.registerForRemoteNotifications()
            return true
          }

Get the device token in delegate methods.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let deviceTokenString = deviceToken.reduce("") { $0 + String(format: "%02X", $1) }
    print("APNs device token: \(deviceTokenString)")
    // Persist it in your backend in case it's new
  }
  
  func application(_ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("APNs registration failed: \(error)")
    
  }

Example Payload

{  
   "aps":{  
      "alert":"How is my demo?",
      "sound":"default",
      "mutable-content":1,
      "badge":1,
      "category":"Feedback"
   },
   "attachment-url":"https://picsum.photos/300/300?image=0"
}

UNNotification Extensions

Apple added 2 more extensions from iOS 10 for notifications

  • Notification Service Extension - For downloading and saving attachment file from URL
  • Notification Content Extension - To display custom UI for notification

Step 3: Adding Notification Service Extension

  1. Select Project Name -> File -> New -> Target -> Notification Service Extension
  2. Enter Product name then click on finish button. It will add Notification Service Extension to the project. (This extension contains NotificationService.swift and info.plist files)

    Change the deployment target to iOS 10.

  3. I modified the didReceive method as per my Payload. You can write your own Parsing here.
    
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)
     {
       self.contentHandler = contentHandler
       bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
       // Modify the notification content here...
       if let bestAttemptContent = bestAttemptContent {
         //Get attachment URL from JSON Payload
         if let urlString = bestAttemptContent.userInfo["attachment-url"] as? String,
           let data = NSData(contentsOf: URL(string:urlString)!) as Data? {
           let path = NSTemporaryDirectory() + "attachment"
           _ = FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
           //Save into temporary Storage
           do {
             let file = URL(fileURLWithPath: path)
             let attachment = try UNNotificationAttachment(identifier: "attachment", url: file,options:[UNNotificationAttachmentOptionsTypeHintKey : "public.jpeg"])
             
             bestAttemptContent.attachments = [attachment]
             
           } catch {
             print(error)
             
           }
         }
         contentHandler(bestAttemptContent)
       }
       
     }
  4. If an attachment fails to download or it takes too long to download an attachment service extension time will expire method will call. You can handle here accordingly.

Up to this point, you will display Rich notifications with default UI.

Note: Use different bundle ID and provisions for extensions.

 

Step 4: Adding Notification Content Extension

  1. Select Project Name -> File -> New -> Target -> Notification Content Extension
  2. Enter Product name then click on finish button. It will add Notification Content Extension to the project. (This extension contains ViewController.swift, Story Board and info.plist files)
  3. You can create your own custom UI in the storyboard. I modified mine as per my requirement. (i.e Image view and text)
  4. Write your own logic to display image.
    
     func didReceive(_ notification: UNNotification) {
      if  let userInfoDic =  notification.request.content.userInfo["aps"] as? [String: Any] {
    
        let alertBody = userInfoDic["alert"] as? String
        self.label?.text = alertBody
      }
    
      if let urlString = notification.request.content.userInfo["attachment-url"], let fileUrl = URL(string: urlString as! String) {
        
        let imageData = NSData(contentsOf: fileUrl)
        let image = UIImage(data: imageData! as Data)!
        
        imageView?.image = image
      }
    }
  5. It will display the notification with our own custom UI. If you want to hide default view set UNNotificationExtensionDefaultContentHidden to YES in info.plist.
How to Add Actions to Notification

You can add instant actions to notification itself.

  1. You can add the following method in Notification service or Notification content or App Delegate.
    func addActions() {
        let likeAction = UNNotificationAction.init(identifier: "like", title: "Like", options: [])
        let dislikeAction = UNNotificationAction.init(identifier: "dislike", title: "Dislike", options: [])
        let feedbackCategory = UNNotificationCategory.init(identifier: "Feedback", actions: [likeAction,dislikeAction], intentIdentifiers: [], options: [])
        UNUserNotificationCenter.current().setNotificationCategories([feedbackCategory])
      } 

    I added below code in Notification service and I called this method in

    func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) method. 

    Note: identifier name must be equal to the category name i.e Feedback as the identifier in this sample app

  2. Add delegate method for button actions in App delegate class.
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        if (response.notification.request.content.categoryIdentifier == "Feedback") {
          if response.actionIdentifier == "like" {
            print("Thank you for Liking")
          }
          if response.actionIdentifier == "dislike" {
            print("Thanks! I will improve")
          }
        }
        completionHandler()
      }  
Points to Remember
  1. Set deployment target to iOS 10 for projects and extensions
  2. Use different bundle IDs
  3. Add App Transport security settings in Notification service extensions for HTTP URLs
  4. Category name must be same as category name in the payload
How to Debug Extensions?

Extension breakpoints can't work directly. So we need to attach the process to debug.

  1. Debug -> Attach to Process by PID or Name
  2. Enter extension name and then click on attach

    Now debugging and breakpoints will work on extensions.

Conclusion

Apple has provided its users with a state of the art feature that can provide notifications that have an engaging content to the end users. Irrespective of the type of application one is developing, a rich notification has more detail and easily grabs users attention with the content completely related to the context. And, this is the USP of the rich notification.

Topics: Mobility

Krishna Prakash Narasannagari

Krishna Prakash Narasannagari

Senior Engineer - Software Engineering

Subscribe to Email Updates

Recent Posts