Implementing Notification Extensions

The most exciting new feature in the realm of notifications in iOS 10 is Notification Extensions. Apple has added two extension points to notifications, which enables us to take the notification experience up a notch from custom actions: Service Extensions and Content Extensions. These extensions are both very powerful and relatively simple to implement. We'll have a look at Service Extensions first.

Adding a Service Extension to your app

The Service Extension is intended to act as middleware for push notifications. The Service Extension receives the notification before it's displayed to the user. This allows you to manipulate or enrich the content before it's displayed.

A Service Extension is perfect if you're implementing end-to-end encryption, for example. Before iOS 10, notifications had to be sent in plain text. This means that any app that implements end-to-end encryption still wasn't completely secure because push notifications were not encrypted. In iOS 10, you can push the encrypted message from your server to the device and decrypt the message in the Service Extension before passing it back to the application.

Another great use of a Service Extension is to download media attachments from a push notification, save it locally, and add it as a media attachment to the notification contents. Remember that all media attachments must be stored locally on the device. This means that a push notification can't really have media attachments unless a Service Extension is used to store the media locally.

To allow a Service Extension to handle a push notification, you must add the mutable-content property to the aps dictionary on the notification:

{
     aps: {
         alert: "You have a new message!", 
         badge: 1, 
         mutable-content: 1 
    }, 
    custom-encrypted-message: "MyEncryptedMessage" 
} 

When the mutable-content property is detected, your Service Extension is activated and receives the notification before it's displayed to the user. A Service Extension is created in the same way as other extensions. You go to the project settings in Xcode and, in the sidebar that shows all targets, you click the plus icon. In the dialog that appears, you select the Notification Service Extension, you give it a name, and Xcode will provide you with the required boilerplate code. A sample extension has been added to The Daily Quote to illustrate what a Service Extension that updates the notification's body text looks like.

Note that the Service Extension has been given rights to the app group we created earlier and the Quote.swift file has been added to the extension target. This enables us to read quotes from the shared UserDefaults.

The boilerplate code that Xcode generates for a Service Extension is rather interesting. Two properties are created, a contentHandler and bestAttemptContent. The properties are initially given a value in didReceive(_:withContentHandler:). This method is called as soon as we're expected to handle the notification.

If we fail to call the callback handler in a timely manner, the system calls serviceExtensionTimeWillExpire(). This is essentially our last chance to quickly come up with content for the notification. If we still fail to call the callback, the message is displayed to the user in its original form. Xcode generated a version of this method for us that uses the stored callback and the current state that our notification is in, and the callback is called immediately.

Let's look at a simple implementation of didReceive(_:withContentHandler:):

override func didReceive(_ request: UNNotificationRequest, 
  withContentHandler contentHandler: @escaping 
  (UNNotificationContent) -> Void) { 
    self.contentHandler = contentHandler 
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) 
     
    if let bestAttemptContent = bestAttemptContent { 
        let todayQuote = Quote.current 
        bestAttemptContent.subtitle = "Quote by (todayQuote.creator)" 
        bestAttemptContent.body = todayQuote.text 
         
        contentHandler(bestAttemptContent) 
    } 
} 

In this snippet, we grab the incoming notification contents and we add a subtitle and a body to it. The quote struct is used to obtain the current quote, and this approach would enable us to push a fresh quote to our user every day, even if the server has no idea which quote should be displayed today. Pretty awesome, right?

Adding a content extension to your app

The last iOS 10 notifications feature that we'll explore is arguably the most awesome, exciting feature that has been added to notifications in iOS. Content extensions enable developers to take custom notification actions to a whole new level. We've already seen that 3D-touching a notification will make custom actions pop up. However, the notification remains the same and possibly doesn't provide as much context as we would like.

Consider receiving an invite for an event. The notification allows you to accept the invite or decline it. Wouldn't it be great if your calendar popped up as well, allowing you to actually check your calendar before responding to the invite? This is what content extensions are for. When implemented correctly, they can provide users with essential information relevant to the notification that's currently on display.

Content extensions are limited in terms of interaction. A content extension itself does not receive any touches; it can only display contents. It can, however, respond to the actions that are associated with the notification. This means that you can update the user interface based on the selected action. A great example of this is the content extension that Apple created for the messages app. You can almost have the full messages experience right inside a notification. You can see new messages appear as they come in and you can respond to them right inside the content extension.

To demonstrate the possibilities of content extensions, we'll take the simple notification we created for The Daily Quote to the next level. Earlier, we scheduled a local notification that simply notified the user about the presence of a new quote. The notification didn't specify what the contents of the quote are or even who the creator of the quote is.

We'll implement a content extension that will reveal the quote and its creator to the user as they 3D-touch on the notification. To do this, we need to add a new extension to our project. This time, you should pick the Notification Content extension.

After adding the extension, Xcode generated some boilerplate code, just like it always does. Before we do anything, you should add the App Groups capability to the content extension and make sure to include the Quote.swift file in the extension target. Doing this allows you to use the shared UserDefaults and fetch the currently selected quote.

Next, replace the existing outlet in the NotificationViewController with the following outlets:

@IBOutlet var quoteLabel: UILabel! 
@IBOutlet var quoteCreator: UILabel! 

Then, remove the existing implementation for viewDidLoad() and add the following implementation for didReceive(_:):

func didReceive(_ notification: UNNotification) { 
    let quote = Quote.current 
    quoteLabel.text = quote.text 
    quoteCreator.text = quote.creator 
} 

Any time our extension is supposed to handle an incoming notification, we simply want to grab the quote for today and show it to the user. In your own application, you probably want to examine the contents of the actual notification to provide more context about the notification. In this use case, we don't need that; we already know what we're supposed to display.

In the storyboard, remove the existing label and add two new labels, one for the quote itself and one for its creator. Give the quote the Title 1 styling and the creator label should have the Caption 1 style. Add constraints so the quote label is constrained to the view's top, left-hand and right-hand sides. Constrain the creator label to the bottom and the sides. You should end up with a view that looks as shown in the following screenshot. Giving the view controller a height of +-100 points will help you position the labels more easily:

Adding a content extension to your app

Finally, open the Info.plist file in your extension. As mentioned before, content extensions are associated to notifications through categories. We use the Info.plist to specify the category our extension belongs to. Expand the NSExtension and NSExtensionAttributes fields to find the UNNotificationExtensionCategory property. Give this field a value of quote.

In the main app's ViewController class, use the following trigger to schedule a notification we can use to test our extension:

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 
  10, repeats: false) 

We're ready to take our extension for a spin. Select your extension in the drop-down menu next to the run and stop controls in the top-left corner of Xcode and run your project. Xcode will ask you for an app to run; pick The Daily Quote. After opening the app, a notification will trigger after 10 seconds. Once the notification comes in, 3D-touch it to see your extension in action:

Adding a content extension to your app

If you've been trying this out yourself, you may have noticed that the notification started out too high and animated to its correct size. The correct size was determined by auto layout because we've added constraints to the top and bottom of our view. However, we would probably like to make sure the amount of animating we need to do is minimal, because it just doesn't look very good.

In the extension's Info.plist, you may have noticed a property called UNNotificationExtensionInitialContentSizeRatio that's right below the Notification Category property. The default value for this property is 1, but our app could probably use a value that's a lot smaller. Try setting this value to 0.3 and run your extension again. It doesn't have to animate nearly as much because the initial height is now just 30 percent of the extension's width. Much better.

You probably also noticed that the original notification contents are visible below our custom view. We can hide this default content by adding the UNNotificationExtensionDefaultContentHidden property to the extension's Info.plist file. All properties you add for your extension should be added at the same level as UNNotificationExtensionInitialContentSizeRatio and UNNotificationExtensionCategory:

Adding a content extension to your app

This wraps up the interface part of our content extension. We can also respond to actions from the notification right inside our extension. To do this, we need to implement the didReceive(_:completionHandler:) delegate method in our extension. Once we implement this method, our extension becomes responsible for all actions that are chosen by the user. This means that our extension should either handle all the actions, or that it should explicitly pass them on to the host app.

After handling the incoming action, the notification extension determines what should happen next. We do this by calling the completion handler with a UNNotificationContentExtensionResponseOption. We have three options to choose from. The first is to simply dismiss the notification. The second is to dismiss the notification and forward the chosen action to the host app. The final option we have is to keep the extension active so the user can pick more actions, or, in the example of messages, so that the user can carry on the conversation inside the content extension.

The following snippet demonstrates a very plain and simple example that prints the action a user chose and forwards it to the app:

func didReceive(_ response: UNNotificationResponse, 
  completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { 
    print(response.actionIdentifier) 
     
    completion(.dismissAndForwardAction) 
} 

As you've seen just now, content extensions are not complex to implement, yet they are amazingly powerful. We can add an entire new layer of interaction and increase the relevance of our notifications in iOS 10, and it is strongly recommended that you always consider ways to implement extensions for your notifications to provide the user with rich information about their notification as quickly as possible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.22.23