Over a million developers have joined DZone.

New Features in Push Notifications

Push notifications are becoming increasingly popular. In this article, the authors explain the Notification Service Extension and Notification Content Extension.

Push notifications are becoming increasingly popular. Vendors like Apple and Google are adding new features to push notifications within their new releases.

Nearly all mobile applications today make use of push notifications in some way, so we will not be discussing the definitions or how-to basics for push notification systems.

In this article, we will examine the new push notification features arrived on iOS 10 and Android N.

Apple introduced support for displaying  image, GIF, video, audio and custom content in notifications with iOS 10 while Android N has new notification styles like messaging style, new notification grouping method like bundled notifications. The most important innovation in Android N notifications is direct reply.

Prior to iOS 10, there was no way to intercept messages between APNs and iOS application. Now, two extensions are available. These extensions are Notification Service Extension and Notification Content Extension.

Notification Service Extension

Notification Service Extension is a layer between APNs and iOS application and runs before iOS shows the notification. It provides customizing the content of notifications like encrypt-decrypt content or download files like image, audio, and video.

Image notification sample:

Image title

GIF notification sample:

Image title

Video notification sample:

Image title

How to Add Notification Service Extension to Your Project

Image title

Image title

When we create service extension, three new files are added to the project folder.

Image title

The Notification Service Extension class has two methods, the first one being didReceive() and the second being serviceExtensionTimeWillExpire().

//
//  UNNotificationServiceExtension.h
//  UserNotifications
//
//  Copyright © 2016 Apple Inc. All rights reserved.
//

@available(iOS 10.0, *)
open class UNNotificationServiceExtension : NSObject {


    // Call contentHandler with the modified notification content to deliver. If the handler is not called before the service's time expires then the unmodified notification will be delivered.
    // You are expected to override this method to implement push notification modification.
    open func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Swift.Void)


    // Will be called just before this extension is terminated by the system. You may choose whether to override this method.
    open func serviceExtensionTimeWillExpire()
}

In didReceive() method, we can download files and add these files to the notification. If this download process takes a certain time, the serviceExtensionTimeWillExpire method runs as a means to handle the timeout.

Notification payload must have mutable-content : 1 key-value field to run service extension.

Our sample NotificationService implementation:

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {

            if let attachmentUrl = bestAttemptContent.userInfo["attachment-url"] as? String {
                var url = URL(string: attachmentUrl)

                downloadFile(url: url!) { localURL in
                    if let localURL = localURL {
                        do {
                            let attachment = try UNNotificationAttachment(identifier: "", url: localURL, options: nil)
                            bestAttemptContent.attachments = [attachment]
                        } catch {
                            print(error)
                        }
                    }
                    contentHandler(bestAttemptContent)
                }
            }
        }
    }

    override func serviceExtensionTimeWillExpire() {

        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

    private func downloadFile(url: URL, handler: @escaping (_ localURL: URL?) -> Void){
        URLSession.shared.downloadTask(with: url, completionHandler: { (location, response, error) in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let location = location, error == nil
                else { return }
            do {
                let documentsUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                let destination = documentsUrl.appendingPathComponent(url.lastPathComponent)
                try FileManager.default.moveItem(at: location, to: destination)
                handler(destination)
            } catch let error as NSError {
                print(error.localizedDescription)
            }
        }).resume()
    }

}

Notification Content Extension

Notifications can be created with custom content. Notification Content Extension provides a way to process the notification content and create a new custom user interface. One disadvantage of content extension is that there's no interaction with the view. For interaction, notification actions must be used.

Image title

How to Add Notification Content Extension to Your Project

Image title

When we create the content extension, three new files are added to the project folder.

Image title

NotificationViewController implements UIViewController and UNNotificationContentExtension.

UNNotificationContentExtension protocol:

@available(iOS 10.0, *)
public protocol UNNotificationContentExtension : NSObjectProtocol {


    // This will be called to send the notification to be displayed by
    // the extension. If the extension is being displayed and more related
    // notifications arrive (eg. more messages for the same conversation)
    // the same method will be called for each new notification.
    public func didReceive(_ notification: UNNotification)


    // If implemented, the method will be called when the user taps on one
    // of the notification actions. The completion handler can be called
    // after handling the action to dismiss the notification and forward the
    // action to the app if necessary.
    optional public func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Swift.Void)


    // Implementing this method and returning a button type other that "None" will
    // make the notification attempt to draw a play/pause button correctly styled
    // for that type.
    optional public var mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType { get }


    // Implementing this method and returning a non-empty frame will make
    // the notification draw a button that allows the user to play and pause
    // media content embedded in the notification.
    optional public var mediaPlayPauseButtonFrame: CGRect { get }


    // The tint color to use for the button.
    @NSCopying optional public var mediaPlayPauseButtonTintColor: UIColor { get }


    // Called when the user taps the play or pause button.
    optional public func mediaPlay()

    optional public func mediaPause()
}

NotificationViewController must implement the didReceive() method. In the didReceive method, you can access the notification content and create your own custom UI according to this content.

The UNNotificationExtensionCategory field of Info.plist file is key to running Notification Content Extension. The category field of the notification payload and UNNotificationExtensionCategory field must be the same to run the content extension.

Image title

Notification Actions

Notification action is the action button below the expanded notification view. We interact with notification through these action buttons.

The UNNotificationAction class must be used to create action button:

let goClosestATMAction = UNNotificationAction(identifier: ActionType.goClosestATM.rawValue, title: "Go Closest ATM", options: UNNotificationActionOptions.foreground)

The UNNotificationAction object must be set to the actions field of UNNotificationCategory:

let mapCategory = UNNotificationCategory(identifier: "myNotificationCategory", actions: [goClosestATMAction], intentIdentifiers: [], options: [])

The UNNotificationCategory object must be set to UNUserNotificationCenter’s categories:

UNUserNotificationCenter.current().setNotificationCategories(Set([mapCategory]))

You can handle the action button click event on userNotificationCenterr method of class, which implements UNUserNotificationCenterDelegate protocol.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

        if response.notification.request.content.categoryIdentifier == "myNotificationCategory" {

            if response.actionIdentifier == ActionType.goClosestATM.rawValue {

            } 
        }

        completionHandler()
    }

If you forget to write the escaping keyword in the method signature, your method won’t be called.

Notes

Your project’s Push Notifications capability must be enabled.

Image title

Registration of push notification, UNUserNotificationCenter delegate, notification action, and notification category assignments can be done in AppDelegate’s application didFinishLaunchingWithOptions method:

   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

        registerPushNotifications()

        UNUserNotificationCenter.current().delegate = notificationHandler

        let goClosestATMAction = UNNotificationAction(identifier: ActionType.goClosestATM.rawValue, title: "Go Closest ATM", options: UNNotificationActionOptions.foreground)

        let goSecondATMAction = UNNotificationAction(identifier: ActionType.goSecondATM.rawValue, title: "Go Second ATM", options: UNNotificationActionOptions.foreground)

        let goThirdATMAction = UNNotificationAction(identifier: ActionType.goThirdATM.rawValue, title: "Go Third ATM", options: UNNotificationActionOptions.foreground)

        let mapCategory = UNNotificationCategory(identifier: "myNotificationCategory", actions: [goClosestATMAction, goSecondATMAction, goThirdATMAction], intentIdentifiers: [], options: [])

        UNUserNotificationCenter.current().setNotificationCategories(Set([mapCategory]))

        return true
    }


    func registerPushNotifications() {

        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
        { (granted, error) in
            let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)
            UIApplication.shared.registerUserNotificationSettings(settings)
        }
    }

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        if notificationSettings.types != UIUserNotificationType() {
            application.registerForRemoteNotifications()
        }
    }

Android N Notifications

Android N arrived with many features for push notifications like direct reply, messaging style, and bundled notifications. Also, the standard view of notifications changed.

Direct Reply

Direct reply allows the notification to receive inline text. Before Android N versions, we used notification action buttons for this purpose.

Direct reply text input is at the bottom of the notification.

Image title

So, you don’t have to open the application to send a reply message.

How to Add Direct Reply to the Notification

First, we must create a RemoteInput object. Then, this object must be set to Notification.Action’s addRemoteInput method. Finally, this Notification.Action object must be set to NotificationBuilder’s addAction method.

RemoteInput remoteInput = new RemoteInput.Builder(Constants.KEY_TEXT_REPLY)
        .setLabel("Reply")
        .build();


Notification.Action action = new Notification.Action.Builder(Icon.createWithResource(getApplicationContext(), R.drawable.reply), "Reply", replyPendingIntent)
        .addRemoteInput(remoteInput)
        .build();

notificationBuilder.addAction(action);

How to Access Reply Input Value From Intent

Bundle remoteInput = RemoteInput.getResultsFromIntent(getIntent());

if (remoteInput != null) {

    Intent intent = getIntent();
    String inputText = remoteInput.getCharSequence(Constants.KEY_TEXT_REPLY).toString();
}

Messaging Style

Another new feature of Android N notifications is Messaging Style. This style is very convenient for messaging applications. You can also display old messages with a new message. Also, you use the direct reply feature with this style.

Image title

How to Use Messaging Style on Notifications

First, we must create a Notification.MessagingStyle object. This class gets displayUserName parameter. You can set a conversation title to appear at the top of the notification. You can add messages that you want to show using the addMessage method of messaging style object. Finally, the messaging object must be set to setStyle method of notification builder.

Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("Me");

messagingStyle.setConversationTitle(messageBodiesList.size() + " new messages with " + senderUserName);

messagingStyle.addMessage(messageBodyElement, System.currentTimeMillis(), senderUserName + ": ");

notificationBuilder.setStyle(messagingStyle);

If you set direct reply action to the addAction method of notification builder, you can use the direct reply feature on this style.

Notes

To receive notifications, the application should get Internet permission. On Andorid N, it isn’t enough to add these permissions to manifest.xml file. You must write code to get these permissions.

ActivityCompat.requestPermissions(this, new String[]{"android.permission.INTERNET"}, 1);

You must override onRequestPermissionsResult method of the Activity class to get user answer for permissions.

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {}
Topics:
push notifications ,mobile ,ios ,android

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}