https://www.raywenderlich.com/8164-push-notifications-tutorial-getting-started
iOS developers love to imagine users using their awesome app constantly. But, of course, users will sometimes have to close the app and perform other activities. Laundry doesn’t fold itself, you know! Happily, push notifications allow developers to reach users and perform small tasks — even when users aren’t actively using an app! In this tutorial, you’ll learn how to configure your app to receive push notifications and to display them to your users.
Push notifications have become more powerful since they were first introduced. With iOS 12, push notifications can:
This tutorial covers many of these. To do this tutorial, you’ll need the following to test push notifications:
To send and receive push notifications, you must perform three main tasks:
Sending push notifications is a responsibility of your app’s server component. Many apps use third parties to send push notifications, while others use custom solutions or popular libraries (e.g., Houston). You’ll only touch on sending push messages in this tutorial, mostly for the purposes of testing the app.
To get started, download the WenderCast starter project using the Download Materials button at the top or bottom of this tutorial. WenderCast is everyone’s go-to source for raywenderlich.com podcasts and breaking news.
In the WenderCast-Starter folder, open WenderCast.xcodeproj with Xcode. Select WenderCast in the Project navigator, then select the WenderCast target. In the General tab, find the Signing section and select your development Team. Build and run on your device:
WenderCast displays a list of podcasts and lets users play them on raywenderlich.com. The problems are that it doesn’t let users know when a new podcast is available and the News tab is empty! You’ll soon fix these issues with the power of push notifications.
Security is very important for push notifications, since you don’t want anyone else to send push notifications to your users through your app. You’ll need to perform several tasks to configure your app to safely receive push notifications.
First, change the App ID. In Xcode, highlight WenderCast in the Project navigator, then select the WenderCast target. Select General, then change Bundle Identifierto something unique so Apple’s push notification server can direct pushes to this app.
Next, you need to create an App ID in your developer account and enable the push notification entitlement. Xcode has a simple way to do this: With the WenderCasttarget still selected, click the Capabilities tab and set the Push Notificationsswitch On.
After loading, it should look like this:
Behind the scenes, this creates the App ID and then adds the push notifications entitlement to it. You can log into the Apple Developer Center to verify this:
That’s all you need to configure for now.
There are two steps you take to register for push notifications. First, you must get the user’s permission to show notifications. Then, you can register the device to receive remote (push) notifications. If all goes well, the system will then provide you with a device token, which you can think of as an “address” to this device.
In WenderCast, you’ll register for push notifications immediately after the app launches. Ask for user permissions, first.
Open AppDelegate.swift and add the following to the top of the file:
import UserNotifications
Then, add the following method to the end of AppDelegate
:
func registerForPushNotifications() {
UNUserNotificationCenter.current() // 1
.requestAuthorization(options: [.alert, .sound, .badge]) { // 2
granted, error in
print("Permission granted: \(granted)") // 3
}
}
Going over the code above:
UNUserNotificationCenter
handles all notification-related activities in the app.requestAuthorization(options:completionHandler:)
to (you guessed it) request authorization to show notifications. The passed options
indicate the types of notifications you want your app to use – here you’re requesting alert, sound and badge.requestAuthorization(options:completionHandler:)
can include any combination of UNAuthorizationOptions
:.badge
: Display a number on the corner of the app’s icon..sound
: Play a sound..alert
: Display text..carPlay
: Display notifications in CarPlay..provisional
: Post non-interrupting notifications. The user won’t get a request for permission if you only use this option, but your notifications will only show silently in the Notification Center..providesAppNotificationSettings
: Indicate that the app has its own UI for notification settings..criticalAlert
: Ignore the mute switch and Do Not Disturb. You’ll need a special entitlement from Apple to use this option as it’s only meant to be used when absolutely necessary.Add the following near the end of application(_:didFinishLaunchingWithOptions:)
, just before the return
:
registerForPushNotifications()
Calling registerForPushNotifications()
here ensures the app will attempt to register for push notifications any time it’s launched.
Build and run. When the app launches, you should receive a prompt that asks for permission to send you notifications.
Tap Allow and poof! The app can now display notifications. Great! But what if the user declines the permissions? Add this method inside AppDelegate
:
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
}
}
First, you specified the settings you want. This method returns the settings the user has granted. For now you’re printing them, but you’ll circle back here shortly.
In registerForPushNotifications, replace the call to requestAuthorization(options:completionHandler:)
with the following:
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] granted, error in
print("Permission granted: \(granted)")
guard granted else { return }
self?.getNotificationSettings()
}
You’ve added a call to getNotificationSettings
in the completion handler. This is important as the user can, at any time, go into the Settings app and change their notification permissions. The guard
avoids making this call in cases where permission wasn’t granted.
Now that you have permissions, you’ll now actually register for remote notifications!
In getNotificationSettings()
, add the following beneath the print
inside the closure:
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
Here, you verify the authorizationStatus
is .authorized
: The user has granted notification permissions. If so, you call UIApplication.shared.registerForRemoteNotifications()
to kick off registration with the Apple Push Notification service. You need to call it on the main thread, or you’ll receive a runtime warning.
Add the following two methods to the end of AppDelegate
. iOS will call these to inform you about the result of registerForRemoteNotifications()
:
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
Once the registration completes, iOS calls application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
if successful. If not, it calls application(_:didFailToRegisterForRemoteNotificationsWithError:)
.
The current implementation of application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
looks cryptic, but it’s simply taking deviceToken
and converting it to a string. The device token is the fruit of this process. It’s provided by APNs and uniquely identifies this app on this particular device. When sending a push notification, the server uses tokens as “addresses” to deliver to the correct devices. In your app, you would now send this token to your server, so that it could be saved and later used for sending notifications.
That’s it! Build and run on a device. You should receive a token in the console output. Here’s an example:
Copy this token, you’ll need it for testing.
You have a bit more configuration to do before you can send a push notification. Head over to the Apple Developer Member Center and log in.
Sending push notifications has gotten easier since Apple’s introduction of Authentication Keys. Previously, you would have to follow a complicated process to create a push notification certificate for each app that needs to send notifications. Now, you only need to create one key, which can be used by any of your apps.
In the member center, select Certificates, Identifiers & Profiles, then find Keys ▸ All on the left hand pane. In the upper-right corner, click on the + button to create a new key.
Give the key a name, such as Push Notification Key. Under Key Services, select Apple Push Notifications service (APNs).
Click Continue and then Confirm on the next screen to create your new key. Tap Download. The downloaded file will be named something like AuthKey_4SVKWF966R.p8. Keep track of this file — you’ll need it to send your notifications! The 4SVKWF966R part of the file name is the Key ID. You’ll also need this.
The final piece that you need is your Team ID. Navigate to the Membership Detailspage of the member center to find it.
Whew! That was a lot to get through, but it was all worth it. With your new key, you are now ready to send your first push notification!
If you haven’t downloaded the PushNotifications utility yet, now’s the time.
Launch PushNotifications and complete the following steps:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com"
}
}
You should receive your first push notification:
There are a couple problems you might encounter:
Before you move on to handling push notifications, take a look at the body of the notification you’ve sent:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com"
}
}
The payload is a JSON dictionary that contains at least one item, aps, which itself is also a dictionary. In this example, aps
contains the fields alert
, sound
, and link_url
. When the device receives this push notification, it shows an alert view with the text “Breaking News!” and plays the standard sound effect.
link_url
is actually a custom field. You can add custom fields to the payload like this and they will get delivered to your application. Since you aren’t handling it inside the app yet, this key/value pair currently does nothing.
There are seven built-in keys you can add to the aps
dictionary:
alert
: This can be a string, like in the previous example, or a dictionary itself. As a dictionary, it can localize the text or change other aspects of the notification.badge
: This is a number that will display in the corner of the app icon. You can remove the badge by setting this to 0.sound
: Name of a custom notification sound’s file located in the app. Custom notification sounds must be shorter than 30 seconds and have a few restrictions.thread-id
: Use this key to group notifications.category
: This defines the category of the notification, which is is used to show custom actions on the notification. You will explore this shortly.content-available
: By setting this key to 1
, the push notification becomes silent. You will learn about this in the Silent Push Notifications section.mutable-content
: By setting this key to 1
, your app can modify the notification before displaying it.Outside of these, you can add as much custom data as you want, as long as the payload does not exceed 4,096 bytes.
Once you’ve had enough fun sending push notifications to your device, move on to the next section!
In this section, you’ll learn how to perform actions when your app receives notifications and when users tap on them.
When your app receives a push notification, iOS calls a method in UIApplicationDelegate
.
You’ll need to handle a notification differently depending on what state your app is in when it’s received:
launchOptions
of application(_:didFinishLaunchingWithOptions:)
.application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
. If the user opens the app by tapping the push notification, iOS may call this method again, so you can update the UI and display relevant information.In the first case, WenderCast will create the news item and open up directly to the News tab. Add the following code to the end of application(_:didFinishLaunchingWithOptions:)
, just before the return statement:
// Check if launched from notification
let notificationOption = launchOptions?[.remoteNotification]
// 1
if let notification = notificationOption as? [String: AnyObject],
let aps = notification["aps"] as? [String: AnyObject] {
// 2
NewsItem.makeNewsItem(aps)
// 3
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
}
This code does three things:
UIApplication.LaunchOptionsKey.remoteNotification
exists in launchOptions
. If it does, then your app was launched from a notification. This will be the push notification payload you sent.aps
dictionary and creates a NewsItem
with its contents.To test this, you need to edit the scheme of WenderCast:
Click on the WenderCast scheme and select Edit Scheme…. Select Run from the sidebar, then in the Info tab select Wait for executable to be launched:
This option will make the debugger wait for the app to be launched for the first time after installing to attach to it.
Build and run. Once it’s done installing, send out more breaking news. Tap on the notification, and the app should open up to news:
To handle the other case for receiving push notifications, add the following method to AppDelegate
:
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler:
@escaping (UIBackgroundFetchResult) -> Void
) {
guard let aps = userInfo["aps"] as? [String: AnyObject] else {
completionHandler(.failed)
return
}
NewsItem.makeNewsItem(aps)
}
This method creates a new NewsItem
from the contents of a push message.
Remember that this method is called when the app is running. Change the scheme back to launching the app automatically. In the Scheme editor, under Launchselect Automatically.
Build and run. Keep the app running in the foreground and on the News tab. Send another news push notification and watch as it appears in the feed:
That’s it! Your app can now magically receive breaking news as it happens.
Something important consider: Many times, push notifications may be missed. This is OK for WenderCast, since having the full list of news isn’t too important for this app. Generally, you should not use push notifications as the only way of delivering content.
Instead, push notifications should signal that there is new content available and let the app download the content from the source (e.g., from a REST API). WenderCast is a bit limited in this sense, as it doesn’t have a true server-side component.
Actionable notifications let you add custom buttons to the notification itself. You may have noticed this on email notifications or Tweets that let you “reply” or “favorite” on the spot.
Your app can define actionable notifications when you register for notifications by using categories. Each category of notification can have a few preset custom actions.
Once registered, your server can set the category of a push notification. The corresponding actions will be available to the user when received.
For WenderCast, you will define a News category with a custom action named View. This action will allow users to view the news article in the app if they choose to.
In registerForPushNotifications()
, insert the following just below the guard and above the call to getNotificationSettings()
:
// 1
let viewAction = UNNotificationAction(
identifier: Identifiers.viewAction, title: "View",
options: [.foreground])
// 2
let newsCategory = UNNotificationCategory(
identifier: Identifiers.newsCategory, actions: [viewAction],
intentIdentifiers: [], options: [])
// 3
UNUserNotificationCenter.current().setNotificationCategories([newsCategory])
Here’s what the new code does:
setNotificationCategories
, register the new actionable notification.That’s it! Build and run the app to register the new notification settings.
Background the app and then send the following payload via the PushNotificationsutility:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com",
"category": "NEWS_CATEGORY"
}
}
If all goes well, you should be able to pull down on the notification to reveal the View action:
Nice! Tapping on it will launch WenderCast, but it won’t do anything. To get it to display the news item, you need to do some more event handling in the delegate.
Whenever a notification action is triggered, UNUserNotificationCenter
informs its delegate. Back in AppDelegate.swift, add the following class extension to the bottom of the file:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
// 1
let userInfo = response.notification.request.content.userInfo
// 2
if let aps = userInfo["aps"] as? [String: AnyObject],
let newsItem = NewsItem.makeNewsItem(aps) {
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
// 3
if response.actionIdentifier == Identifiers.viewAction,
let url = URL(string: newsItem.link) {
let safari = SFSafariViewController(url: url)
window?.rootViewController?.present(safari, animated: true,
completion: nil)
}
}
// 4
completionHandler()
}
}
This is the callback you get when the app is opened by a custom action. It might look like there’s a lot going on, but there’s not much new, here:
aps
dictionary.NewsItem
from the dictionary and navigate to the News tab.identifier
. If it is the “View” action and the link is a valid URL, it displays the link in an SFSafariViewController
.There is one last bit: You have to set the delegate on UNUserNotificationCenter
. Add this line to the top of application(_:didFinishLaunchingWithOptions:)
:
UNUserNotificationCenter.current().delegate = self
Build and run. Close the app again, then send another news notification with the following payload:
{
"aps": {
"alert": "New Posts!",
"sound": "default",
"link_url": "https://raywenderlich.com",
"category": "NEWS_CATEGORY"
}
}
Pull down the notification and tap on the View action, and you should see WenderCast present a Safari View controller right after it launches:
Congratulations, you’ve implemented an actionable notification! Send a few more and try opening the notification in different ways to see how it behaves.
Silent push notifications can wake your app up silently to perform some tasks in the background. WenderCast can use this feature to quietly refresh the podcast list.
With a proper server component this can be very efficient. Your app won’t need to constantly poll for data. You can send it a silent push notification whenever new data is available.
To get started, select the WenderCast target again. Now tap the Capabilities tab and set the Background Modes switch On. Then check the Remote notificationsoption:
Now, your app will wake up in the background when it receives one of these push notifications.
Inside AppDelegate
, find application(_:didReceiveRemoteNotification:)
. Replace the call to NewsItem.makeNewsItem()
with the following:
// 1
if aps["content-available"] as? Int == 1 {
let podcastStore = PodcastStore.sharedStore
// 2
podcastStore.refreshItems { didLoadNewItems in
// 3
completionHandler(didLoadNewItems ? .newData : .noData)
}
} else {
// 4
NewsItem.makeNewsItem(aps)
completionHandler(.newData)
}
Going over the code:
content-available
is set to 1, to see if it is a silent notification.Be sure to call the completion handler with the honest result. The system measures the battery consumption and time that your app uses in the background and may throttle your app if needed.
That’s all there is to it; to test it, build and run, foreground the app and push the following payload via PushNotifications:
{
"aps": {
"content-available": 1
}
}
If all goes well, nothing should happen, unless a new podcast was just added to the remote database. To confirm the code ran as expected, set a breakpoint in application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
and step through it after a push.
Congratulations! You’ve completed this tutorial and made WenderCast a fully featured app with push notifications!
You can download the completed project using the Download Materials button at the top or bottom of this tutorial.
Want to dive deeper into everything you can do with push notifications, like building custom UIs and sending critical alerts? Our Push Notifications by Tutorials book will teach you the advanced features of push notifications.
Even though push notifications are an important part of apps nowadays, it’s also very common for users to decline permissions to your app if it sends notifications too often. But with thoughtful design, push notifications can keep your users coming back to your app again and again!
I hope you’ve enjoyed this push notifications tutorial. If you have any questions, feel free to leave them in the discussion forum below.