Notifications is a way to inform users when new data becomes available for the app, even when the app is not running in the foreground.
For example, a messaging app might let the user know when a new message has arrived, and a calendar app might inform the user of an upcoming appointment.
With the release of iOS-10, Apple introduces brand new frameworks to support notifications, be it local or remote. Customized notifications is what this release focussed on.
Without wasting any time, let’s just quickly jump on to the details.
We can broadly classify notifications into 2 categories,
Further in the article, we’ll see how we can get hold of both the notification types. Let’s first start with the introduction to the very new notification framework that we can use to our cause.
With the release of iOS-10, Apple introduced 2 new frameworks to handle notifications,
We’ll be using these 2 frameworks and some platform-specific APIs to configure our notifications.
Along with the frameworks, Notification service app extension was also introduced that allows modifying the content of remote notifications before they are delivered.
Apple also allowed customising the notifications UI though Notification content extension.
Is it too much to remember? Yup..surely it is. But, don't worry. We’ll see everything step-by-step along with the relevant code. Just take it easy..😉
To get our app notify anything, we need to know whether the person using it actually wants that at the first place. May be he don’t like his phone ringing and displaying alerts all the time 😖..or may be he actually want the updates but not that irritating sound..naahhh..!!!☠️
So, first of all we need to take the permission from the one we’re going to notify. And that’s pretty simple, just 2 lines of code and we’re done.
<a href="https://medium.com/media/8a4c84b1e71564b571dac07c3ac16838/href">https://medium.com/media/8a4c84b1e71564b571dac07c3ac16838/href</a>
You need to write that code in AppDelegate’s method — application:didFinishLaunchingWithOptions:before returning from it.
Point to be noted: Because the system saves the user’s response, calls to requestAuthorization(options:completionHandler:) method during subsequent launches do not prompt the user again.
The user notifications framework supports adding categories and actions to the notifications.
Categories — Define the types of notifications that the app supports and communicate to the system how we want a notification to be presented.
Actions — Each category can have up to four actions associated with it. Actions are basically custom buttons, that on tap dismisses the notification interface and forwards the selected action to app for immediate handling.
Okayyy..!!! And what does that mean..????🤔 Some code might help you understand that better.
<a href="https://medium.com/media/beafb0f3bd1aa031cce11f0fcd8c8be0/href">https://medium.com/media/beafb0f3bd1aa031cce11f0fcd8c8be0/href</a>
In the above code, we simply created a category named INVITATION with 4 different actions — remindLater, accept, decline, comment.
The categories and actions are uniquely identified by their identifiers. Whenever a notification with a category is delivered, the system presents the notification along with all the actions associated with that category once the user expands it. This is what it will look like 👇,
Define all the categories and actions just below where you configured notifications in application:didFinishLaunchingWithOptions: method.
Include the category identifier (eg. INVITATION) while scheduling your notification whether locally or remotely. We’ll see how to do that in the next section.
Now that we’re done with configuring our notifications, let’s see how to actually schedule one from within the app.
Scheduling a local notification is just 3 simple steps,
Let’s get on with the code quickly, so we don’t get confused with everything happening here. LOL 😝
<a href="https://medium.com/media/91dd7a6561d4e202f7519e4a7d9d4ef9/href">https://medium.com/media/91dd7a6561d4e202f7519e4a7d9d4ef9/href</a>
In the above code along with the other content, we have also provided a categoryIdentifier to support actionable notifications. In case we don’t do that, the system will adopt it’s default behavior.
That’s it. That’s all needed. And yes it definitely works..hehehe..😝 Give it a try before moving any further. You can download the sample from here.
App behaves differently in background and foreground states whenever a notification is delivered.
When app is in foreground while the notification is delivered, we get the callback in UNUserNotificationCenterDelegate's method — userNotificationCenter(_:willPresent:withCompletionHandler:) where you can decide whether to handle the notification silently or alert the user about it.
<a href="https://medium.com/media/09367e53c539bcf28a80b2db6f28f6a2/href">https://medium.com/media/09367e53c539bcf28a80b2db6f28f6a2/href</a>
Don’t forget to conform AppDelegate to UNUserNotificationCenterDelegate protocol and setting it as the delegate of UNUserNotificationCenter shared object in application:didFinishLaunchingWithOptions:.
let center = UNUserNotificationCenter.current()
center.delegate = self
We’re done with local notifications for now. Let’s move on to how we can schedule a notification from outside our app. Before that, let’s have a look on how to respond to the custom actions.
Configuring notifications?? ✔ Scheduling notifications?? ✔
What about tapping a notification or any custom action in the notification? Where will it lead to? — in both the cases, system notifies the app of the user’s choice.
Whenever the user performs any action in the notification, the response is sent to UNUserNotificationCenterDelegate's method — userNotificationCenter(_:didReceive:withCompletionHandler:), where we can provide handling specific to each action.
<a href="https://medium.com/media/ece91657a4780f66f94ed85aa46447ea/href">https://medium.com/media/ece91657a4780f66f94ed85aa46447ea/href</a>
Point to be noted — if the app is not running when a response is received, the system launches the app in the background to process the response.
Push notification or remote notifications, no matter what we call it, it’s one of the most frequestly used one with lots and lots of use-cases.
Be it social media or calendar or any of the utilities app, we could see them almost everywhere. News apps notifying us of the latest content, medium itself alerting us of the latest published articles.
Ever wondered how do they even do that? Local Notifications ???🤔 It could be..it does the same thing..right? May be we can do some more configuration in the local one itself and get that working? But medium for example, don’t have access to the app on our personal device, so how could it schedule any notification? Exactly!!! It can’t. This is something different and something more just than the local ones.
Send the notification from some point and show it at some other point — will this answer our question? Yupp..surely it will. But how to do that? Remote Notifications it is. This is exactly what it does. This is the feature that has solved THE BIG PROBLEM of “Keeping up-to-date”.
Never cache device tokens in your app; instead, get them from the system when you need them.
APNs issues a new device token to your app when certain events happen. The device token is guaranteed to be different, for example, when a user restores a device from a backup, when the user installs your app on a new device, and when the user reinstalls the operating system.
When you attempt to fetch a device token but it has not changed, the fetch method returns quickly.
Point to be noted — The ability of APNs to deliver remote notifications to a nonrunning app requires the app to have been launched at least once.
Below is a small and quick explanation of how all the above technologies work together in sync to complete the remote notifications workflow.
If a notification for your app arrives with the device powered on but with the app not running, the system can still display the notification. If the device is powered off when APNs sends a notification, APNs holds on to the notification and tries again later.
Now that we are aware of what remote notifications are and what all things are needed to make it work, let’s now move on to how we can make our app support it. Obviously, nothing happens on its own 😉. We need to make some configurations for the same. To be able to handle remote notifications, our app must:
2. Register with Apple Push Notification service (APNs) and receive an app-specific device token
Requesting to register with APNs is quick and easy. Just add the below code in UIApplicationDelegate’s method— application:didFinishLaunchingWithOptions: before returning from it.
UIApplication.shared.registerForRemoteNotifications()
Now there can be 2 possibilities, either we get registered successfully or the process fails.
On successful registration, APNs sends an app-specific device token to the device inUIApplicationDelegate’s method— application:didRegisterForRemoteNotificationsWithDeviceToken:.
In case of faliure, we receive a callback in UIApplicationDelegate’s method—application:didFailToRegisterForRemoteNotificationsWithError:.
<a href="https://medium.com/media/fa91b639b7417fa7b0ebc94ae00175bb/href">https://medium.com/media/fa91b639b7417fa7b0ebc94ae00175bb/href</a>
3. Send the device token to notification provider server
As of now, we’ve received the device token from APNs. Now, we need to send this token to our provider, which will use it while pushing any notifications to our device.
Since we don’t have a provider, for now we can use Easy APNs Provider for testing our push notifications. Further in the sections, we’ll see how exactly we can make use of this tool.
For now, just download and install it on your mac.
4. Implement support for handling incoming remote notifications
We have got our device token and our provider also knows about it. Next, the Provider will send the notification including this token and other information in it and we’ll get it on our device.
Now what? What will happen when it arrives? How will it appear on the device? What will happen when we tap on it? What about all the actions that we configured earlier? Can we get them here?
Too many question ❓❓❓..Well, don’t worry. We’ll have answers to all of them one-by-one.
We have covered most of the things we need to integrate push notifications into our app. Although we know how to handle it in the app, we are still short of handling it on the provider.
We have the provider. It knows what device token to use, but that solely won’t pop a notification on our device with some title and other details. Neither will it make any of the actions appear. So, pushing notifications from the provider needs the following items,
<a href="https://medium.com/media/f2fc013833dad4036ae9bc49b78a08f5/href">https://medium.com/media/f2fc013833dad4036ae9bc49b78a08f5/href</a>
Let’s see what’s all in that JSON dictionary,
Here you can read more about customizing the payload as per your requirements. This is a reference to the keys that we can add in aps dictionary
Uptill now, we know what remote notifications are..how they work..what all we need to get them working..everything..we just got them working perfectly..✌️.
Now question is, what if we want to modify some content in the notification received from provider, before presenting it on the device? What if the notification contains some image link that we need to download before delivering it to the user? Can we do that with what all we already know? We don’t have access to the provider..so how will we?
We can’t actually. We can’t change what we get..but we can definitely change what we present.
That’s what Notification Service App Extension is all about— modifying the content of remote notification before delivery. It is as simple as it looks like. No fancy code..nothing..very simple.
Extensions in an xcode project are added as a target. Select File — New — Target — Notification Service Extension.
Before we begin to modify the content, there are some restrictions on when the content is allowed to be modified. Content can be modified only if:
We cannot modify silent notifications or those that only play a sound or badge the app’s icon.
Hence, to support any modifications in the notifications’ content, these conditions must be fulfilled.
The default notification service extension target provided by Xcode contains a subclass of the UNNotificationServiceExtension class for us to modify.
It contains 2 methods:
Let’s look at an example. We’ll change the body in payload in Code Snippet — 7 to “Address: Sea Shells Apartments, Mumbai”.
<a href="https://medium.com/media/e43d7ac3370b49cc2552952a3e2f5530/href">https://medium.com/media/e43d7ac3370b49cc2552952a3e2f5530/href</a>
All the default implementation of both the methods is provided by the extension itself. We just have to make the changes we want, like in Line — 8 in the above code snippet. Just a single line of code for now. Similarly, you can modify other fields as per your requirements.
Having an eye-catching UI is any time better than a default simple UI. Adding some colors..some pretty fonts is never a bad idea. We’re going to do the same with our notifications, make them look Woww..!!!😍
And and and…Apple is here to our rescue again. Notification content extension it is — presents a custom interface for a delivered local or remote notification.
I think we already know how to do that. Isn’t it? We’re going to the same what we did for adding Notification Service Extension. Select File — New — Target — Notification Content Extension.
To support custom UI for local & remote notifications, we need to make some changes in the Info.plist file of content extension.
Here is an illustration that can help us understand the above keys better.
In the above illustration, the keys in Info.plist are configured as:
Notification content extension, provide us with a UIViewController that conforms to UNNotificationContentExtension protocol. This controller presents the interface of the notification. The Storyboard file in the extension contains a single ViewController that we can use to create whatever UI we want the notification to present.
Once we create the UI, we need to connect the elements in the NotificationViewController in order to fill in the details. Whenever a notification arrives with an expected category, we receive a callback in UNNotificationContentExtension’s method — didReceive(_:) . This is the place where we can add details to our customised UI.
<a href="https://medium.com/media/b6a10f6863550817a4c3ac06e1f57f74/href">https://medium.com/media/b6a10f6863550817a4c3ac06e1f57f74/href</a>
We’re almost done with our notification’s custom UI. Just 1 more thing. Since the custom UI is attached to the notifications’ category that may have some actions attached to it. And..you got that right..!!! 🤘We’ll get our actions automatically without any custom handling. Brilliant..!!!👏
Content + Beautiful UI + Custom Actions — Everything done. What more can we ask for. Apple..you are great..!!!🤩
Last point, we can add handling to the custom actions in the extension too. The system calls didReceive(_:completionHandler:) method to respond to any selected actions. If our view controller doesn’t implement that method, the system delivers the selected action to your app for handling.
<a href="https://medium.com/media/2a0b65ae058b1c3c4f6f14ada4cf903b/href">https://medium.com/media/2a0b65ae058b1c3c4f6f14ada4cf903b/href</a>
If implemented, we need to handle all the possible actions in this method. One thing that is important here is the completion closure.
completion
The block to execute when you are finished performing the action. You must call this block at some point during your implementation. The block has no return value
The closure accepts a single parameter dismiss of type UNNotificationContentExtensionResponseOption . We provide the following options:
That sums up our notifications. Too much to remember? Practise makes Progress 🙂. Try making your own notifications.
You can download the sample project from here.
Sample project for Notification Content Extension can be found here.
Don’t forget to read my other articles:
Feel free to leave comments in case you have any doubts.