Push Notifications Tutorial: Getting Started
Push notifications allow developers to reach users, 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 or perform other tasks. By Chuck Krutsinger .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Push Notifications Tutorial: Getting Started
35 mins
- Getting Started
- Sending and Receiving Push Notifications
- Configuring the App
- Enabling the Push Notification Service
- Asking for User Notifications Permission
- Registering With APNs
- Sending a Simulated Push Notification
- Looking at a Basic Push Notification
- Handling Push Notifications
- Understanding What Happens When You Receive a Push Notification
- Working With Actionable Notifications
- Handling Notification Actions
- Sending to a Real Device
- Troubleshooting Common Issues
- Using Silent Push Notifications
- Where to Go From Here?
iOS developers love to imagine people 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 the app! In this tutorial, you’ll learn how to:
- Configure your app to receive push notifications.
- Display them to your users or perform other tasks.
Getting Started
What are push notifications? They are messages sent to your app through the Apple Push Notification service (APNs) even if your app isn’t running or the phone is sleeping. What can you use push notifications for?
- Display a short text message, called an alert, that draws attention to something new in your app.
- Play a notification sound.
- Set a badge number on the app’s icon to let the user know there are new items.
- Provide actions the user can take without opening the app.
- Show a media attachment.
- Be silent, allowing the app to perform a task in the background.
- Group notifications into threads.
- Edit or remove delivered notifications.
- Run code to change your notification before displaying it.
- Display a custom, interactive UI for your notification.
- And probably more.
This tutorial covers many of these uses to help you get started creating push notifications in your apps. To complete this tutorial, you’ll need the following:
- Xcode 11.4 or later. Earlier versions of Xcode don’t support push notifications using the simulator.
- An Apple Developer Program membership to be able to compile the app with the Push Notifications entitlement.
To send and receive push notifications, you must perform three main tasks:
- Configure your app and register it with the APNs.
- Send a push notification from a server to specific devices via APNs. You’ll simulate that with Xcode.
- Use callbacks in the app to receive and handle push notifications.
Sending push notifications is a responsibility of your app’s server component. Many apps use third parties to send push notifications. Others use custom solutions or popular libraries (such as Houston). You’ll only touch on sending push messages in this tutorial, so be sure to check out the Where to Go From Here? section to build on your knowledge of push notifications.
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 starter folder, open WenderCast.xcodeproj. Select WenderCast in the Project navigator, then select the WenderCast target. In the General & Capabilities tab, select your development team. Build and run in a simulator.
WenderCast displays a list of raywenderlich.com podcasts and lets users play them. But 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.
Sending and Receiving Push Notifications
Configuring the App
Security is very important for push notifications. 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 securely receive push notifications.
Enabling the Push Notification Service
First, you have to change the bundle identifier. In Xcode, highlight WenderCast in the Project navigator, then select the WenderCast target. Select General, then change Bundle Identifier to 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 WenderCast target still selected, click the Signing & Capabilities tab and then click the + Capability button. Type “push” in the filter field and press Enter.
After adding the push notifications entitlement, your project 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. You are ready to start enhancing the app.
Asking for User Notifications Permission
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() {
//1
UNUserNotificationCenter.current()
//2
.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
//3
print("Permission granted: \(granted)")
}
}
What this code does:
-
UNUserNotificationCenter
handles all notification-related activities in the app, including push notifications. - You invoke
requestAuthorization(options:completionHandler:)
to (you guessed it) request authorization to show notifications. The passedoptions
indicate the types of notifications you want your app to use — here you’re requesting alert, sound and badge. - The completion handler receives a Bool that indicates whether authorization was successful. In this case, you simply print the result.
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 a text notification.
- .carPlay: Display notifications in CarPlay.
- .provisional: Post non-interrupting notifications. The user won’t get a request for permission if you use only 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, because it’s meant only for very special use cases.
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 to do more with this.
In registerForPushNotifications()
, replace the call to requestAuthorization(options:completionHandler:)
with the following:
UNUserNotificationCenter.current()
.requestAuthorization(
options: [.alert, .sound, .badge]) { [weak self] granted, _ 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 because 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.
Registering With APNs
Now that you have permissions, you’ll 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 this on the main thread, or you’ll receive a runtime warning.
Add the following to the end of AppDelegate
:
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)")
}
This method is called by iOS whenever a call to registerForRemoteNotifications()
succeeds. The code may look cryptic, but it’s simply taking a received 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 to save and use later on for sending notifications.
Now add the following:
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
This method is called by iOS if registerForRemoteNotifications()
fails. You’re just printing the error for now.
That’s it! Build and run. Because you are on a simulator, you’ll see a Failed to register
error. You can ignore that for now. Later, when you run on a real device, you should receive a token in the console output. Here’s an example:
Sending a Simulated Push Notification
Use a text editor to create a file called first.apn, which you’ll pass to Xcode’s simctl utility. Paste in the following JSON text and save the file.
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com"
}
}
The structure of this JSON will be explained in the next section. Patience, grasshopper.
Build and run the app again on the simulator, then background the app or lock the device. The app is not yet able to process push notifications while in the foreground.
To use simctl, you’ll need to know the device identifier for the simulator that you are running the app in. If there is only one device running in the simulator, you can use booted instead of an identifier. To get the identifier, in Xcode, select Windows ▸ Devices and Simulators, then select the Simulators tab at the top and select the simulator you’re using from the list on the left. Use your mouse to copy the identifier. You might need to widen the dialog box to see it fully.
Open the Terminal app and change to the directory where you saved first.apn. Then type in the following command using either booted or the device identifier from Xcode in place of device_identifier: xcrun simctl push device_identifier bundle_identifier first.apn
. Replace device_identifier
with the device identifier you copied from Xcode and replace bundle_identifier
with the app’s bundle identifier — the one you used when you first set up the project. Here’s an example:
Run the command and you’ll see the push notification appear on the simulator!
Tap on the notification to launch the app.
Isn’t that cool? :]
Looking at a Basic Push Notification
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 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. Because you aren’t handling it inside the app yet, this key/value pair currently does nothing.
link_url
should be at a peer level to the aps
dictionary entry rather than inside of it. It still works if you place it inside, but we always suggest adhering to Apple’s documentation and will correct this in our next update to this tutorial.There are eight built-in keys you can add to the aps
dictionary (see the official Payload Key Reference for more information):
- alert: This can be a string, as in the previous example, or a dictionary. 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. These must be shorter than 30 seconds.
- thread-id: Use this key to group notifications.
- category: This defines the category of the notification, which is used to show custom actions on the notification. You’ll explore this shortly.
-
content-available: By setting this key to
1
, the push notification becomes silent. You’ll learn about this in the Silent Push Notifications section below. -
mutable-content: By setting this key to
1
, your app can modify the notification before displaying it. - target-content-id: This is the identifier of the window brought forward.
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 trying out these options and sending push notifications to your simulator, move on to the next section!
Handling Push Notifications
In this section, you’ll learn how to perform actions when your app receives notifications and when users tap them.
Understanding What Happens When You Receive a Push Notification
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 the notification is received:
- If your app wasn’t running and the user launches it by tapping the push notification, iOS passes the notification to your app in the
launchOptions
ofapplication(_:didFinishLaunchingWithOptions:)
. - If your app was running either in the foreground or the background, the system notifies your app by calling
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
. When 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 directly to the News tab. In AppDelegate.swift, 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 is what you’re doing:
- Check whether the value for
UIApplication.LaunchOptionsKey.remoteNotification
exists inlaunchOptions
. If it does, then your app was launched from a notification. This will contain the push notification payload you sent. - Since the
aps
dictionary exists, create aNewsItem
with it. - Change the selected tab of the tab controller to the News section.
To test this, you need to edit the scheme of WenderCast. First, build and run to install the latest code on the simulator. Then, click 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 using xcrun simctl
as before. Tap the notification, and the app will open to news:
To handle the situation where your app is running when a push notification is received, add the following 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 tries to extract the aps from the supplied userInfo
object and, if successful, creates a new NewsItem
from it.
Since iOS calls this method when the app is running, you need to change the scheme back to launching the app automatically to test it. In the Scheme editor, under Launch, select 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 now can magically receive breaking news as it happens. :]
Working With Actionable Notifications
Actionable notifications let you add custom buttons to the notification itself. You might 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’ll 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])
Going through this, step-by-step:
- Create a new notification action, with the title View on the button, that opens the app in the foreground when triggered. The action has a distinct identifier, which iOS uses to differentiate between other actions on the same notification.
- Define the news category, which will contain the view action. This also has a distinct identifier that your payload will need to contain to specify that the push notification belongs to this category.
- Register the new actionable notification by calling
setNotificationCategories
.
Build and run the app to register the new notification settings.
Background the app and then send the following payload via the xcrun simctl
utility:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com",
"category": "NEWS_CATEGORY"
}
}
When the notification appears, pull down on it to reveal the View action:
Nice! Tapping View will launch WenderCast, but it doesn’t do anything exciting just yet. To get it to display the news item, you need to do some more event handling in the delegate.
Handling Notification Actions
Whenever a notification action gets triggered, UNUserNotificationCenter
informs its delegate. Back in AppDelegate.swift, add the following class extension to the bottom of the file:
// MARK: - UNUserNotificationCenterDelegate
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 opens because of a custom action. It might look like there’s a lot going on, but there’s not much new here:
- Get the
userInfo
dictionary. - Create a
NewsItem
from theaps
dictionary and navigate to the News tab. - Check the
actionIdentifier
. If it is the “View” action and the link is a valid URL, it displays the link in anSFSafariViewController
. - Call the completion handler the system passes to you.
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 the View action, and you’ll 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.
Sending to a Real Device
If you don’t want to send push notifications to a real device or you don’t need silent push notifications yet, you can skip this section and go to Where to Go From Here?
However, if you want to get a feel for how to send push notifications to a real device and try silent push, then you need to do some additional setup. Download the PushNotifications utility. You’ll use this utility app to send notifications to a real device. To install it, follow the instructions under How to install. Pay special attention to how to open the app because you’ll have to change some settings to run this utility.
Head over to the Apple Developer Member Center and log in.
Sending push notifications requires an Authentication Key. In the member center, select Certificates, Identifiers & Profiles, then find Keys on the left pane. To the right of the Keys title is a + button. Click it 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 Register on the next screen to create your new key. Tap Download. The downloaded file will have a name 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 Details page of the member center to find it.
You did it! With your new key, you are now ready to send your first push notification! You need just one more thing.
Run the app on your real device and copy the device token from the debugger console and have it ready.
Launch PushNotifications and complete the following steps:
- Under Authentication, select Token.
- Click the Select P8 button and select the .p8 file from the previous section.
- Enter your Key ID and Team ID in the relevant fields.
- Under Body, enter your app’s Bundle ID and your device token.
- Change the request body to look like this:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://raywenderlich.com"
}
}
Click the Send button in PushNotifications.
You should receive your push notification:
Troubleshooting Common Issues
Here are a couple problems you might encounter:
- Some notifications arrive, but not all: If you’re sending many push notifications simultaneously but you receive only a few, fear not! That is by design. APNs maintains a QoS (Quality of Service) queue for each device. The size of this queue is 1, so if you send multiple notifications, the last notification is overridden.
- Problem connecting to Push Notification Service: One possibility could be that there is a firewall blocking the ports used by APNs. Make sure you unblock these ports.
Using Silent Push Notifications
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 click the Signing & Capabilities tab and add the Background Modes capability. Then check the Remote notifications option:
Now, your app will wake up in the background when it receives one of these push notifications.
In AppDelegate.swift, find application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
. 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:
- You check to see if
content-available
is set to 1. If so, this is a silent notification. - You refresh the podcast list, which is an asynchronous network call.
- When the refresh is complete, call the completion handler to let the system know whether the app loaded any new data.
- If it isn’t a silent notification, then it is a news item, so make a news item.
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 the PushNotifications utility:
{
"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 the notification is sent.
Where to Go From Here?
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, such as building custom UIs and sending critical alerts? Our Push Notifications by Tutorials book will teach you the advanced features of push notifications.
Another resource is the Push Notifications Tutorial for iOS: Rich Push Notifications tutorial.
Even though push notifications are an important part of modern apps, it’s also very common for users to decline permissions to your app if you overdo the notifications. 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.