{"id":862,"date":"2018-03-30T07:43:40","date_gmt":"2018-03-30T07:43:40","guid":{"rendered":"http:\/\/adriangrigoras.com\/blog\/?p=862"},"modified":"2018-03-30T07:43:40","modified_gmt":"2018-03-30T07:43:40","slug":"push-notifications-tutorial-started","status":"publish","type":"post","link":"https:\/\/adriangrigoras.com\/blog\/push-notifications-tutorial-started\/","title":{"rendered":"Push Notifications Tutorial: Getting Started"},"content":{"rendered":"<p>iOS developers love to imagine users of their awesome app using the app all day, every day. Unfortunately, the cold hard truth is that users will sometimes have to close the app and perform other activities. Laundry doesn\u2019t fold itself, you know :]<\/p>\n<p>Happily,\u00a0<em>push notifications<\/em>\u00a0allow developers to reach users and perform small tasks even when users aren\u2019t actively using an app!<\/p>\n<p>Push notifications have become more and more powerful since they were first introduced. In iOS 10, push notifications can:<\/p>\n<ul>\n<li>Display a short text message<\/li>\n<li>Play a notification sound<\/li>\n<li>Set a badge number on the app\u2019s icon<\/li>\n<li>Provide actions the user can take without opening the app<\/li>\n<li>Show a media attachment<\/li>\n<li>Be silent, allowing the app to wake up in the background and perform a task<\/li>\n<\/ul>\n<p>This push notifications tutorial will go over how push notifications work, and let you try out their features.<\/p>\n<p>Before you get started, you will need the following to test push notifications:<\/p>\n<ul>\n<li><em>An iOS device<\/em>. Push notifications do not work in the simulator, so you\u2019ll need an actual device.<\/li>\n<li><em>An Apple Developer Program Membership<\/em>. Since Xcode 7, you can test apps on your device without a program membership. However, to configure push notifications you need a push notification certificate for your App ID, which requires the program membership.<\/li>\n<li><em>Pusher<\/em>. you\u2019ll use this utility app to send notifications to the device. To install, follow the instructions\u00a0<a href=\"https:\/\/github.com\/noodlewerk\/NWPusher\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/li>\n<\/ul>\n<h2>Getting Started<\/h2>\n<p>There are three main tasks that must be performed in order to send and receive a push notification:<\/p>\n<ol>\n<li>The app must be configured properly and registered with the Apple Push Notification Service (APNS) to receive push notifications upon every start-up.<\/li>\n<li>A server must send a push notification to APNS directed to one or more specific devices.<\/li>\n<li>The app must receive the push notification; it can then perform tasks or handle user actions using callbacks in the application delegate.<\/li>\n<\/ol>\n<p>Tasks 1 and 3 will be the main focus of this push notifications tutorial, since they are the responsibility of an iOS developer.<\/p>\n<p>Task 2 will also be briefly covered, mostly for testing purposes. Sending push notifications is a responsibility of the app\u2019s server-component and is usually implemented differently from one app to the next. Many apps use third-parties (you can find some good examples\u00a0<a href=\"https:\/\/www.raywenderlich.com\/126098\/top-5-parse-alternatives\" target=\"_blank\" rel=\"noopener\">here<\/a>) to send push notifications, while others use custom solutions and\/or popular libraries (ex.\u00a0<a href=\"https:\/\/github.com\/nomad\/houston\" target=\"_blank\" rel=\"noopener\">Houston<\/a>).<\/p>\n<p>To get started, download the\u00a0<a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/05\/WenderCast-Starter.zip\">starter project<\/a>\u00a0of\u00a0<em>WenderCast<\/em>. WenderCast is everyone\u2019s go-to source for raywenderlich.com podcasts and breaking news.<\/p>\n<p>Open\u00a0<em>WenderCast.xcodeproj<\/em>\u00a0in Xcode and take a peek around. Build and run within the iPhone simulator to see the latest podcasts (you\u2019ll use a real device soon!):<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/initial_list.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160270\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/initial_list-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/initial_list-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/initial_list-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/initial_list.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<p>The problem with the app is that it doesn\u2019t let users know when a new podcast is available. It also doesn\u2019t really have any news to display. You\u2019ll soon fix all that with the power of push notifications!<\/p>\n<h2>Configuring the Push Notifications Tutorial App<\/h2>\n<p>Push notifications require a\u00a0<i>lot<\/i>\u00a0of security. This is quite important, since you don\u2019t want anyone else to send push notifications to your users. Unfortunately, this means there\u2019s several required tasks to configure apps for push notifications.<\/p>\n<h3>Enabling the Push Notification Service<\/h3>\n<p>The first step is to change the App ID. Go to\u00a0<em>App Settings -&gt; General<\/em>\u00a0and change\u00a0<em>Bundle Identifier<\/em>\u00a0to something unique:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-162548\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/05\/Screen-Shot-2017-05-15-at-23.05.39-1-650x163.png\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/05\/Screen-Shot-2017-05-15-at-23.05.39-1-650x163.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/05\/Screen-Shot-2017-05-15-at-23.05.39-1-480x120.png 480w\" alt=\"push notifications tutorial\" width=\"650\" height=\"163\" \/><\/p>\n<p>Within\u00a0<em>Signing<\/em>\u00a0right below this, select your development\u00a0<em>Team<\/em>. Again, this must be a paid developer account. If you don\u2019t see any teams, you\u2019ll first need to add your development team via\u00a0<em>Xcode -&gt; Preferences -&gt; Accounts -&gt; +<\/em>.<\/p>\n<p>Next, you need to create an App ID in your developer account that has the push notification entitlement enabled. Luckily, Xcode has a simple way to do this. Go to\u00a0<em>App Settings -&gt; Capabilities<\/em>\u00a0and flip the switch for\u00a0<em>Push Notifications<\/em>\u00a0to\u00a0<em>On<\/em>.<\/p>\n<p>After some loading, it should look like this:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_capabilities.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160274\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_capabilities-480x97.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_capabilities-480x97.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_capabilities-650x131.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_capabilities.png 994w\" alt=\"push notifications tutorial\" width=\"480\" height=\"97\" \/><\/a><\/p>\n<p>If any issues occur, visit the\u00a0<a href=\"https:\/\/developer.apple.com\/account\" target=\"_blank\" rel=\"noopener\">Apple Developer Center<\/a>. You may simply need to agree to a new developer license, which Apple loves to update ;], and try again. Worse case, you may need to manually add the push notifications entitlement by using the\u00a0<em>+<\/em>\u00a0and\u00a0<em>Edit<\/em>\u00a0buttons.<\/p>\n<p>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 and verify this:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160276\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable-454x500.png\" sizes=\"auto, (max-width: 454px) 100vw, 454px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable-454x500.png 454w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable-291x320.png 291w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable.png 1436w\" alt=\"push notifications tutorial\" width=\"454\" height=\"500\" \/><\/a><\/p>\n<p>That\u2019s all you need to configure for now.<\/p>\n<h3>Registering for Push Notifications<\/h3>\n<p>There are two steps to register for push notifications. First, you must obtain the user\u2019s permission to show\u00a0<i>any<\/i>\u00a0kind of notification, after which you can register for remote notifications. If all goes well, the system will then provide you with a\u00a0<em>device token<\/em>, which you can think of as an \u201caddress\u201d to this device.<\/p>\n<p>In WenderCast, you will register for push notifications immediately after the app launches.<\/p>\n<p>Open\u00a0<em>AppDelegate.swift<\/em>\u00a0and add the following import to the top of the file:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-keyword\">import<\/span> UserNotifications\r\n<\/pre>\n<p>Then add the following method to the end of\u00a0<code>AppDelegate<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">registerForPushNotifications<\/span><span class=\"hljs-params\">()<\/span><\/span> {\r\n  <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().requestAuthorization(options: [.alert, .sound, .badge]) {\r\n    (granted, error) <span class=\"hljs-keyword\">in<\/span>\r\n    <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Permission granted: <span class=\"hljs-subst\">\\(granted)<\/span>\"<\/span>)\r\n  }\r\n}\r\n<\/pre>\n<p>Lastly, add a call to\u00a0<code>registerForPushNotifications()<\/code>\u00a0at the end of\u00a0<code>application(_:didFinishLaunchingWithOptions:)<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">application<\/span><span class=\"hljs-params\">(\r\n  <span class=\"hljs-number\">_<\/span> application: UIApplication,\r\n  didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)<\/span><\/span> -&gt; <span class=\"hljs-type\">Bool<\/span> {\r\n  <span class=\"hljs-comment\">\/\/ ... existing code ...<\/span>\r\n  registerForPushNotifications()\r\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>\r\n}\r\n<\/pre>\n<p>Let\u2019s go over the above:\u00a0<code>UNUserNotificationCenter<\/code>\u00a0was introduced in iOS 10 within the UserNotifications framework. It\u2019s responsible for managing all notification-related activities within the app.<\/p>\n<p>You invoke\u00a0<code>requestAuthorization(options:completionHandler:)<\/code>\u00a0to (you guessed it) request authorization for push notifications. Here, you must specify the notification types your app will use. These types (represented by\u00a0<code>UNAuthorizationOptions<\/code>) can be any combination of the following:<\/p>\n<ul>\n<li><code>.badge<\/code>\u00a0allows the app to display a number on the corner of the app\u2019s icon.<\/li>\n<li><code>.sound<\/code>\u00a0allows the app to play a sound.<\/li>\n<li><code>.alert<\/code>\u00a0allows the app to display text.<\/li>\n<li><code>.carPlay<\/code>\u00a0allows the app to display notifications in a CarPlay environment.<\/li>\n<\/ul>\n<p>You call\u00a0<code>registerForPushNotifications<\/code>\u00a0within\u00a0<code>application(_:didFinishLaunchingWithOptions:)<\/code>\u00a0to ensure the demo app will attempt to register for push notifications any time it\u2019s launched.<\/p>\n<p>Build and run. When the app launches, you should receive a prompt that asks for permission to send you notifications.<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7303.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160277\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7303-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7303-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7303-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7303.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<p>Tap\u00a0<em>OK<\/em>\u00a0and poof! The app can now display notifications. Great! But what now? What if the user declines the permissions? Add this method inside\u00a0<code>AppDelegate<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">getNotificationSettings<\/span><span class=\"hljs-params\">()<\/span><\/span> {\r\n  <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().getNotificationSettings { (settings) <span class=\"hljs-keyword\">in<\/span>\r\n    <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Notification settings: <span class=\"hljs-subst\">\\(settings)<\/span>\"<\/span>)\r\n  }\r\n}\r\n<\/pre>\n<p>This method is\u00a0<i>very different<\/i>\u00a0from the previous one. In the previous method, you specified the settings you\u00a0<i>want<\/i>, yet this one returns the settings the user has\u00a0<i>granted<\/i>.<\/p>\n<p>It\u2019s important to call\u00a0<code>getNotificationSettings(completionHandler:)<\/code>\u00a0within the completion handler on\u00a0<code>requestAuthorization<\/code>, which happens whenever the app finishes launching. This is because the user can, at any time, go into the Settings app and change the notification permissions.<\/p>\n<p>Update\u00a0<code>requestAuthorization<\/code>\u00a0to call\u00a0<code>getNotificationSettings()<\/code>\u00a0within the completion closure like this:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">registerForPushNotifications<\/span><span class=\"hljs-params\">()<\/span><\/span> {\r\n  <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().requestAuthorization(options: [.alert, .sound, .badge]) {\r\n    (granted, error) <span class=\"hljs-keyword\">in<\/span>\r\n    <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Permission granted: <span class=\"hljs-subst\">\\(granted)<\/span>\"<\/span>)\r\n\r\n    <span class=\"hljs-keyword\">guard<\/span> granted <span class=\"hljs-keyword\">else<\/span> { <span class=\"hljs-keyword\">return<\/span> }\r\n    <span class=\"hljs-keyword\">self<\/span>.getNotificationSettings()\r\n  }\r\n}\r\n<\/pre>\n<p>Step 1 is now complete, and you\u2019re now ready to actually register for remote notifications!<\/p>\n<p>Update\u00a0<code>getNotificationSettings()<\/code>\u00a0with the following:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">getNotificationSettings<\/span><span class=\"hljs-params\">()<\/span><\/span> {\r\n  <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().getNotificationSettings { (settings) <span class=\"hljs-keyword\">in<\/span>\r\n    <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Notification settings: <span class=\"hljs-subst\">\\(settings)<\/span>\"<\/span>)\r\n    <span class=\"hljs-keyword\">guard<\/span> settings.authorizationStatus == .authorized <span class=\"hljs-keyword\">else<\/span> { <span class=\"hljs-keyword\">return<\/span> }\r\n    <span class=\"hljs-type\">UIApplication<\/span>.shared.registerForRemoteNotifications()\r\n  }\r\n}\r\n<\/pre>\n<p>Here you verify the\u00a0<code>authorizationStatus<\/code>\u00a0is\u00a0<code>.authorized<\/code>, meaning the user has granted notification permissions, and if so, you call\u00a0<code>UIApplication.shared.registerForRemoteNotifications()<\/code>.<\/p>\n<p>Add the following two methods to then end of\u00a0<code>AppDelegate<\/code>; these will be called to inform you about the result of\u00a0<code>registerForRemoteNotifications<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">application<\/span><span class=\"hljs-params\">(<span class=\"hljs-number\">_<\/span> application: UIApplication,\r\n                 didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)<\/span><\/span> {\r\n  <span class=\"hljs-keyword\">let<\/span> tokenParts = deviceToken.<span class=\"hljs-built_in\">map<\/span> { data -&gt; <span class=\"hljs-type\">String<\/span> <span class=\"hljs-keyword\">in<\/span>\r\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-type\">String<\/span>(format: <span class=\"hljs-string\">\"%02.2hhx\"<\/span>, data)\r\n  }\r\n  \r\n  <span class=\"hljs-keyword\">let<\/span> token = tokenParts.joined()\r\n  <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Device Token: <span class=\"hljs-subst\">\\(token)<\/span>\"<\/span>)\r\n}\r\n\r\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">application<\/span><span class=\"hljs-params\">(<span class=\"hljs-number\">_<\/span> application: UIApplication,\r\n                 didFailToRegisterForRemoteNotificationsWithError error: Error)<\/span><\/span> {\r\n  <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Failed to register: <span class=\"hljs-subst\">\\(error)<\/span>\"<\/span>)\r\n}\r\n<\/pre>\n<p>As the names suggest, the system calls\u00a0<code>application(_:didRegisterForRemoteNotificationsWithDeviceToken:)<\/code>\u00a0if the registration is successful, or else it calls\u00a0<code>application(_:didFailToRegisterForRemoteNotificationsWithError:)<\/code>.<\/p>\n<p>The current implementation of\u00a0<code>application(_:didRegisterForRemoteNotificationsWithDeviceToken:)<\/code>looks cryptic, but it is simply taking\u00a0<code>deviceToken<\/code>\u00a0and converting it to a string. The device token is the fruit of this process. It is a token provided by APNS that uniquely identifies this app on this particular device. When sending a push notification, the app uses device tokens as \u201caddresses\u201d to deliver notifications to the correct devices.<\/p>\n<div class=\"note\">\n<p><em>Note:<\/em>\u00a0There are several reasons why registration might fail. Most of the time it\u2019s because the app is running on a simulator, or because the App ID configuration was not done properly. The error message generally provides a good hint for what\u2019s wrong.<\/p>\n<\/div>\n<p>That\u2019s it! Build and run. Make sure you are running on a device, and you should receive a device token in the console output. Here\u2019s what mine looks like:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_device_token.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160278\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_device_token-480x20.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_device_token-480x20.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_device_token-650x27.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_device_token.png 1092w\" alt=\"push notifications tutorial\" width=\"480\" height=\"20\" \/><\/a><\/p>\n<p>Copy this token somewhere handy.<\/p>\n<p>You have a bit more configuration to do before you can send a push notification, so head over to the\u00a0<a href=\"https:\/\/developer.apple.com\/account\" target=\"_blank\" rel=\"noopener\">Apple Developer Member Center<\/a>\u00a0and log in.<\/p>\n<h3>Creating an SSL Certificate and PEM file<\/h3>\n<p>In your member center, go to\u00a0<em>Certificates, IDs &amp; Profiles -&gt; Identifiers -&gt; App IDs<\/em>\u00a0and select the App ID for your app. Under\u00a0<em>Application Services<\/em>, Push Notifications should show as\u00a0<em>Configurable<\/em>:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable_notifications.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160281\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable_notifications-480x31.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable_notifications-480x31.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable_notifications-650x43.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_configurable_notifications.png 1008w\" alt=\"push notifications tutorial\" width=\"480\" height=\"31\" \/><\/a><\/p>\n<p>Click\u00a0<em>Edit<\/em>\u00a0and scroll down to\u00a0<em>Push Notifications<\/em>:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_create_cert.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160280\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_create_cert-650x410.png\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_create_cert-650x410.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_create_cert-480x303.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_create_cert.png 1306w\" alt=\"push notifications tutorial\" width=\"650\" height=\"410\" \/><\/a><\/p>\n<p>In\u00a0<em>Development SSL Certificate<\/em>, click\u00a0<em>Create Certificate\u2026<\/em>\u00a0and follow the steps to create a\u00a0<em>CSR<\/em>. Once you have your CSR, click\u00a0<em>continue<\/em>\u00a0and follow the steps to\u00a0<em>Generate<\/em>\u00a0your certificate using the CSR. Finally, download the certificate and double-click it, which should add it to your Keychain, paired with a private key:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_keychain.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160282\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_keychain-650x51.png\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_keychain-650x51.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_keychain-480x37.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_keychain.png 1486w\" alt=\"push notifications tutorial\" width=\"650\" height=\"51\" \/><\/a><\/p>\n<p>Back in the member center, your App ID should now have push notifications enabled for development:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_enabled.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160285\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_enabled-480x30.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_enabled-480x30.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_enabled-650x40.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_enabled.png 1028w\" alt=\"push notifications tutorial\" width=\"480\" height=\"30\" \/><\/a><\/p>\n<p>Whew! That was a lot to get through, but it was all worth it \u2014 with your new certificate file, you are now ready to send your first push notification!<\/p>\n<h2>Sending a Push Notification<\/h2>\n<p>Sending push notifications requires an SSL connection to APNS, secured by the push certificate you just created. That\u2019s where\u00a0<em>Pusher<\/em>\u00a0comes in.<\/p>\n<p>Launch\u00a0<em>Pusher<\/em>. The app will automatically check for push certificates in the Keychain, and list them in a dropdown. Complete the following steps:<\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li>Select your push certificate from the dropdown.<\/li>\n<li>Paste your device token into the \u201cDevice push token\u201d field.<\/li>\n<li>Modify the request body to look like this:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre class=\"language-json hljs\" lang=\"json\">{\r\n  <span class=\"hljs-attr\">\"aps\"<\/span>: {\r\n    <span class=\"hljs-attr\">\"alert\"<\/span>: <span class=\"hljs-string\">\"Breaking News!\"<\/span>,\r\n    <span class=\"hljs-attr\">\"sound\"<\/span>: <span class=\"hljs-string\">\"default\"<\/span>,\r\n    <span class=\"hljs-attr\">\"link_url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/raywenderlich.com\"<\/span>\r\n  }\r\n}\r\n<\/pre>\n<ul>\n<li>On the device you previously ran WenderCast on, background the app or lock the device, or else it won\u2019t work<em>*<\/em><\/li>\n<li>Click the\u00a0<em>Push<\/em>\u00a0button in Pusher.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/Screen-Shot-2017-04-30-at-13.02.25.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160288\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/Screen-Shot-2017-04-30-at-13.02.25-650x312.png\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/Screen-Shot-2017-04-30-at-13.02.25-650x312.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/Screen-Shot-2017-04-30-at-13.02.25-480x230.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/Screen-Shot-2017-04-30-at-13.02.25.png 1342w\" alt=\"push notifications tutorial\" width=\"650\" height=\"312\" \/><\/a><\/p>\n<p>You should receive your first push notification:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7304.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160287\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7304-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7304-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7304-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7304.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<div class=\"note\">\n<p><em>*Note:<\/em>\u00a0You won\u2019t see anything if the app is open and running in the foreground. The notification is delivered, but there\u2019s nothing in the app to handle it yet. Simply close the app and send the notification again.<\/p>\n<\/div>\n<h3>Common Issues<\/h3>\n<p>There are a couple problems that might arise:<\/p>\n<p><em>Some notifications received but not all:<\/em>\u00a0If you\u2019re sending multiple push notifications simultaneously and only a few are received, fear not! That is intended behaviour. APNS maintains a QoS (Quality of Service) queue for each device with a push app. The size of this queue is 1, so if you send multiple notifications, the last notification is overridden.<\/p>\n<p><em>Problem connecting to Push Notification Service:<\/em>\u00a0One possibility could be that there is a firewall blocking the ports used by APNS. Make sure you unblock these ports. Another possibility might be that the private key and CSR file are wrong. Remember that each App ID has a unique CSR and private key combination.<\/p>\n<h3>Anatomy of a Basic Push Notification<\/h3>\n<p>Before you move on to Task 3, handling push notifications, take a look at the body of the notification you\u2019ve just sent:<\/p>\n<pre class=\"language-json hljs\" lang=\"json\">{\r\n  <span class=\"hljs-attr\">\"aps\"<\/span>: {\r\n    <span class=\"hljs-attr\">\"alert\"<\/span>: <span class=\"hljs-string\">\"Breaking News!\"<\/span>,\r\n    <span class=\"hljs-attr\">\"sound\"<\/span>: <span class=\"hljs-string\">\"default\"<\/span>,\r\n    <span class=\"hljs-attr\">\"link_url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/raywenderlich.com\"<\/span>\r\n  }\r\n}\r\n<\/pre>\n<p>For the JSON-uninitiated, a block delimited by curly { } brackets contains a dictionary that consists of key\/value pairs (just like a Swift\u00a0<code>Dictionary<\/code>).<\/p>\n<p>The payload is a dictionary that contains at least one item,\u00a0<em>aps<\/em>, which itself is also a dictionary. In this example, \u201caps\u201d contains the fields\u00a0<code>alert<\/code>,\u00a0<code>sound<\/code>, and\u00a0<code>link_url<\/code>. When this push notification is received, it shows an alert view with the text \u201cBreaking News!\u201d and plays the standard sound effect.<\/p>\n<p><code>link_url<\/code>\u00a0is 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\u2019t handling it inside the app yet, this key\/value pair currently does nothing.<\/p>\n<p>There are six keys you can add to the\u00a0<code>aps<\/code>\u00a0dictionary:<\/p>\n<ul>\n<li><code>alert<\/code>. 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.<\/li>\n<li><code>badge<\/code>. This is a number that will display in the corner of the app icon. You can remove the badge by setting this to 0.<\/li>\n<li><code>thread-id<\/code>. You may use this key for grouping notifications.<\/li>\n<li><code>sound<\/code>. By setting this key, you can play custom notification sounds located in the app in place of the default notification sound. Custom notification sounds must be shorter than 30 seconds and have a few restrictions.<\/li>\n<li><code>content-available<\/code>. By setting this key to\u00a0<code>1<\/code>, the push notification becomes a silent one. This will be explored later in this push notifications tutorial.<\/li>\n<li><code>category<\/code>. This defines the category of the notification, which is is used to show custom actions on the notification. You will also be exploring this shortly.<\/li>\n<\/ul>\n<p>Outside of these, you can add as much custom data as you want, as long as the payload does not exceed the maximum size of 4096 bytes.<\/p>\n<p>Once you\u2019ve had enough fun sending push notifications to your device, move on to the next section. :]<\/p>\n<h2>Handling Push Notifications<\/h2>\n<p>In this section, you\u2019ll learn how to perform actions in your app when push notifications are received and\/or when users tap on them.<\/p>\n<h3>What Happens When You Receive a Push Notification?<\/h3>\n<p>When your app receives a push notification, a method in\u00a0<code>UIApplicationDelegate<\/code>\u00a0is called.<\/p>\n<p>The notification needs to be handled differently depending on what state your app is in when it\u2019s received:<\/p>\n<ul>\n<li>If your app wasn\u2019t running and the user launches it by tapping the push notification, the push notification is passed to your app in the\u00a0<code>launchOptions<\/code>\u00a0of\u00a0<code>application(_:didFinishLaunchingWithOptions:)<\/code>.<\/li>\n<li>If your app was running either in the foreground, or the background,\u00a0<code>application(_:didReceiveRemoteNotification:fetchCompletionHandler:)<\/code>\u00a0will be called. If the user opens the app by tapping the push notification, this method may be called again, so you can update the UI, and display relevant information.<\/li>\n<\/ul>\n<p>In the first case, WenderCast will create the news item and open up directly to the news section. Add the following code to the end of\u00a0<code>application(_:didFinishLaunchingWithOptions:)<\/code>, before the return statement:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-comment\">\/\/ Check if launched from notification<\/span>\r\n<span class=\"hljs-comment\">\/\/ 1<\/span>\r\n<span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">let<\/span> notification = launchOptions?[.remoteNotification] <span class=\"hljs-keyword\">as<\/span>? [<span class=\"hljs-type\">String<\/span>: <span class=\"hljs-type\">AnyObject<\/span>] {\r\n  <span class=\"hljs-comment\">\/\/ 2<\/span>\r\n  <span class=\"hljs-keyword\">let<\/span> aps = notification[<span class=\"hljs-string\">\"aps\"<\/span>] <span class=\"hljs-keyword\">as<\/span>! [<span class=\"hljs-type\">String<\/span>: <span class=\"hljs-type\">AnyObject<\/span>]\r\n  <span class=\"hljs-number\">_<\/span> = <span class=\"hljs-type\">NewsItem<\/span>.makeNewsItem(aps)\r\n  <span class=\"hljs-comment\">\/\/ 3<\/span>\r\n  (window?.rootViewController <span class=\"hljs-keyword\">as<\/span>? <span class=\"hljs-type\">UITabBarController<\/span>)?.selectedIndex = <span class=\"hljs-number\">1<\/span>\r\n}\r\n<\/pre>\n<p>This code does three things:<\/p>\n<ol>\n<li>It checks whether the value for\u00a0<code>UIApplicationLaunchOptionsKey.remoteNotification<\/code>\u00a0exists in\u00a0<code>launchOptions<\/code>. If it does, this will be the push notification payload you sent.<\/li>\n<li>If it exists, you grab the\u00a0<code>aps<\/code>\u00a0dictionary and pass it to\u00a0<code>createNewNewsItem(_:)<\/code>, which is a helper method provided to create a\u00a0<code>NewsItem<\/code>\u00a0from the dictionary and refresh the news table.<\/li>\n<li>Lastly, it changes the selected tab of the tab controller to\u00a0<code>1<\/code>, the news section.<\/li>\n<\/ol>\n<p>To test this, you need to edit the scheme of WenderCast:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_editscheme.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160290\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_editscheme-480x191.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_editscheme-480x191.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_editscheme.png 588w\" alt=\"push notifications tutorial\" width=\"480\" height=\"191\" \/><\/a><\/p>\n<p>Under\u00a0<em>Run -&gt; Info<\/em>, select\u00a0<em>Wait for executable to be launched<\/em>:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_scheme.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-160289\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_scheme-480x288.png\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_scheme-480x288.png 480w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_scheme-650x390.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_scheme.png 774w\" alt=\"push notifications tutorial\" width=\"480\" height=\"288\" \/><\/a><\/p>\n<p>This option will make the debugger wait for the app to be launched for the first time after installing to attach to it.<\/p>\n<p>Build and run. Once it\u2019s done installing, send out some breaking news again. Tap on the notification, and the app should open up to some news:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7306.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160292\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7306-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7306-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7306-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7306.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<div class=\"note\">\n<p><em>Note:<\/em>\u00a0If you stop receiving push notifications, it is likely that your device token has changed. This can happen if you uninstall and reinstall the app. Double check the device token to make sure.<\/p>\n<\/div>\n<p>To handle the other case, add the following method to\u00a0<code>AppDelegate<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">application<\/span><span class=\"hljs-params\">(\r\n  <span class=\"hljs-number\">_<\/span> application: UIApplication,\r\n  didReceiveRemoteNotification userInfo: [AnyHashable : Any],\r\n  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult)<\/span><\/span> -&gt; <span class=\"hljs-type\">Void<\/span>) {\r\n  \r\n  <span class=\"hljs-keyword\">let<\/span> aps = userInfo[<span class=\"hljs-string\">\"aps\"<\/span>] <span class=\"hljs-keyword\">as<\/span>! [<span class=\"hljs-type\">String<\/span>: <span class=\"hljs-type\">AnyObject<\/span>]\r\n  <span class=\"hljs-number\">_<\/span> = <span class=\"hljs-type\">NewsItem<\/span>.makeNewsItem(aps)\r\n}\r\n<\/pre>\n<p>This method directly uses the helper function to create a new\u00a0<code>NewsItem<\/code>. You can now change the scheme back to launching the app automatically if you like.<\/p>\n<p>Build and run. Keep the app running in the foreground and on the News section. Send another news push notification and watch as it magically appears in the feed:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7308.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160293\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7308-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7308-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7308-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7308.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<p>That\u2019s it! Your app can now handle breaking news in this basic way.<\/p>\n<p>Something important consider: many times, push notifications may be missed. This is okay for WenderCast, since having the full list of news isn\u2019t too important for this app, but in general you should not use push notifications as the only way of delivering content.<\/p>\n<p>Instead, push notifications should signal that there is new content\u00a0<i>available<\/i>\u00a0and 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\u2019t have a true server-side component.<\/p>\n<h3>Actionable Notifications<\/h3>\n<p>Actionable notifications let you add custom buttons to the notification itself. You may have noticed this on email notifications or Tweets that let you \u201creply\u201d or \u201cfavorite\u201d on the spot.<\/p>\n<p>Actionable notifications are defined by your app when you register for notifications by using\u00a0<em>categories<\/em>. Each category of notification can have a few preset custom actions.<\/p>\n<p>Once registered, your server can set the category of a push notification; the corresponding actions will be available to the user when received.<\/p>\n<p>For WenderCast, you will define a \u201cNews\u201d category with a custom action named \u201cView\u201d which allows users to directly view the news article in the app if they choose to.<\/p>\n<p>Replace\u00a0<code>registerForPushNotifications()<\/code>\u00a0in the\u00a0<code>AppDelegate<\/code>\u00a0with the following:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">registerForPushNotifications<\/span><span class=\"hljs-params\">()<\/span><\/span> {\r\n  <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().requestAuthorization(options: [.alert, .sound, .badge]) {\r\n    (granted, error) <span class=\"hljs-keyword\">in<\/span>      \r\n    <span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"Permission granted: <span class=\"hljs-subst\">\\(granted)<\/span>\"<\/span>)\r\n    \r\n    <span class=\"hljs-keyword\">guard<\/span> granted <span class=\"hljs-keyword\">else<\/span> { <span class=\"hljs-keyword\">return<\/span> }\r\n    \r\n    <span class=\"hljs-comment\">\/\/ 1<\/span>\r\n    <span class=\"hljs-keyword\">let<\/span> viewAction = <span class=\"hljs-type\">UNNotificationAction<\/span>(identifier: viewActionIdentifier,\r\n                                          title: <span class=\"hljs-string\">\"View\"<\/span>,\r\n                                          options: [.foreground])\r\n    \r\n    <span class=\"hljs-comment\">\/\/ 2<\/span>\r\n    <span class=\"hljs-keyword\">let<\/span> newsCategory = <span class=\"hljs-type\">UNNotificationCategory<\/span>(identifier: newsCategoryIdentifier,\r\n                                              actions: [viewAction],\r\n                                              intentIdentifiers: [],\r\n                                              options: [])\r\n    <span class=\"hljs-comment\">\/\/ 3<\/span>\r\n    <span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().setNotificationCategories([newsCategory])\r\n    \r\n    <span class=\"hljs-keyword\">self<\/span>.getNotificationSettings()\r\n  }\r\n}\r\n<\/pre>\n<p>Here\u2019s what the new code does:<\/p>\n<ol>\n<li>Here you create a new notification action, with the title\u00a0<em>View<\/em>\u00a0on the button, that opens the app in the foreground when triggered. The action has a distinct identifier, which is used to differentiate between other actions on the same notification.<\/li>\n<li>Next, you define the news category, which will contain the view action. It also has a distinct identifier, which your payload will need to contain to specify, that the push notification belongs to this category.<\/li>\n<li>Finally, by invoking\u00a0<code>setNotificationCategories(_:)<\/code>, you register the new actionable notification.<\/li>\n<\/ol>\n<p>That\u2019s it! Build and run the app to register the new notification settings.<\/p>\n<p>Background the app and then send the following payload via\u00a0<em>Pusher<\/em>:<\/p>\n<pre class=\"language-json hljs\" lang=\"json\">{\r\n  <span class=\"hljs-attr\">\"aps\"<\/span>: {\r\n    <span class=\"hljs-attr\">\"alert\"<\/span>: <span class=\"hljs-string\">\"Breaking News!\"<\/span>,\r\n    <span class=\"hljs-attr\">\"sound\"<\/span>: <span class=\"hljs-string\">\"default\"<\/span>,\r\n    <span class=\"hljs-attr\">\"link_url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/raywenderlich.com\"<\/span>,\r\n    <span class=\"hljs-attr\">\"category\"<\/span>: <span class=\"hljs-string\">\"NEWS_CATEGORY\"<\/span>\r\n  }\r\n}\r\n<\/pre>\n<p>If all goes well, you should be able to pull down on the notification to reveal the View action:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7309.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160294\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7309-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7309-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7309-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7309.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<p>Nice! Tapping on it will launch WenderCast, but it won\u2019t do anything. To get it to display the news item, you need to do some more event handling in the delegate.<\/p>\n<h3>Handling Notification Actions<\/h3>\n<p>Whenever a notification action is triggered,\u00a0<code>UNUserNotificationCenter<\/code>\u00a0informs its delegate. Back in\u00a0<em>AppDelegate.swift<\/em>, add the following class extension to the bottom of the file:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">extension<\/span> <span class=\"hljs-title\">AppDelegate<\/span>: <span class=\"hljs-title\">UNUserNotificationCenterDelegate<\/span> <\/span>{\r\n  \r\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">userNotificationCenter<\/span><span class=\"hljs-params\">(<span class=\"hljs-number\">_<\/span> center: UNUserNotificationCenter,\r\n                              didReceive response: UNNotificationResponse,\r\n                              withCompletionHandler completionHandler: @escaping ()<\/span><\/span> -&gt; <span class=\"hljs-type\">Void<\/span>) {\r\n    <span class=\"hljs-comment\">\/\/ 1<\/span>\r\n    <span class=\"hljs-keyword\">let<\/span> userInfo = response.notification.request.content.userInfo\r\n    <span class=\"hljs-keyword\">let<\/span> aps = userInfo[<span class=\"hljs-string\">\"aps\"<\/span>] <span class=\"hljs-keyword\">as<\/span>! [<span class=\"hljs-type\">String<\/span>: <span class=\"hljs-type\">AnyObject<\/span>]\r\n    \r\n    <span class=\"hljs-comment\">\/\/ 2<\/span>\r\n    <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">let<\/span> newsItem = <span class=\"hljs-type\">NewsItem<\/span>.makeNewsItem(aps) {\r\n      (window?.rootViewController <span class=\"hljs-keyword\">as<\/span>? <span class=\"hljs-type\">UITabBarController<\/span>)?.selectedIndex = <span class=\"hljs-number\">1<\/span>\r\n      \r\n      <span class=\"hljs-comment\">\/\/ 3<\/span>\r\n      <span class=\"hljs-keyword\">if<\/span> response.actionIdentifier == viewActionIdentifier,\r\n        <span class=\"hljs-keyword\">let<\/span> url = <span class=\"hljs-type\">URL<\/span>(string: newsItem.link) {\r\n        <span class=\"hljs-keyword\">let<\/span> safari = <span class=\"hljs-type\">SFSafariViewController<\/span>(url: url)\r\n        window?.rootViewController?.present(safari, animated: <span class=\"hljs-literal\">true<\/span>, completion: <span class=\"hljs-literal\">nil<\/span>)\r\n      }\r\n    }\r\n    \r\n    <span class=\"hljs-comment\">\/\/ 4<\/span>\r\n    completionHandler()\r\n  }\r\n}\r\n<\/pre>\n<p>This is the callback you get when the app is opened by a custom action. It might look like there\u2019s a lot going on, but there\u2019s really not much new here:<\/p>\n<ol>\n<li>Get the\u00a0<code>aps<\/code>\u00a0dictionary.<\/li>\n<li>Create the\u00a0<code>NewsItem<\/code>\u00a0from the dictionary and navigate to the News section.<\/li>\n<li>Check the action identifier, which is passed in as\u00a0<code>identifier<\/code>. If it is the \u201cView\u201d action and the link is a valid URL, it displays the link in a\u00a0<code>SFSafariViewController<\/code>.<\/li>\n<li>Call the completion handler that is passed to you by the system after handling the action.<\/li>\n<\/ol>\n<p>There is one last bit: you have to set the delegate on\u00a0<code>UNUserNotificationCenter<\/code>. Add this line to the top of\u00a0<code>application(_:didFinishLaunchingWithOptions:)<\/code>:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-type\">UNUserNotificationCenter<\/span>.current().delegate = <span class=\"hljs-keyword\">self<\/span>\r\n<\/pre>\n<p>Build and run. Close the app again, then send another news notification with the following payload:<\/p>\n<pre class=\"language-json hljs\" lang=\"json\">{\r\n  <span class=\"hljs-attr\">\"aps\"<\/span>: {\r\n    <span class=\"hljs-attr\">\"alert\"<\/span>: <span class=\"hljs-string\">\"New Posts!\"<\/span>,\r\n    <span class=\"hljs-attr\">\"sound\"<\/span>: <span class=\"hljs-string\">\"default\"<\/span>,\r\n    <span class=\"hljs-attr\">\"link_url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/raywenderlich.com\"<\/span>,\r\n    <span class=\"hljs-attr\">\"category\"<\/span>: <span class=\"hljs-string\">\"NEWS_CATEGORY\"<\/span>\r\n  }\r\n}\r\n<\/pre>\n<p>Tap on the action, and you should see WenderCast present a Safari View Controller right after it launches:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7310.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160295\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7310-281x500.png\" sizes=\"auto, (max-width: 281px) 100vw, 281px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7310-281x500.png 281w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7310-180x320.png 180w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/IMG_7310.png 750w\" alt=\"push notifications tutorial\" width=\"281\" height=\"500\" \/><\/a><\/p>\n<p>Congratulations, you\u2019ve just implemented an actionable notification! Send a few more and try opening the notification in different ways to see how it behaves.<\/p>\n<h3>Silent Push Notifications<\/h3>\n<p>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.<\/p>\n<p>As you can imagine, with a proper server-component this can be very efficient. Your app won\u2019t need to poll for data constantly \u2014 you can send it a silent push notification whenever new data is available.<\/p>\n<p>To get started, go to\u00a0<em>App Settings -&gt; Capabilites<\/em>\u00a0and turn on\u00a0<em>Background Modes<\/em>\u00a0for WenderCast. Check the last option,\u00a0<em>Remote Notifications<\/em>:<\/p>\n<p><a href=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_bg.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-160296\" src=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_bg-650x188.png\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" srcset=\"https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_bg-650x188.png 650w, https:\/\/koenig-media.raywenderlich.com\/uploads\/2017\/04\/screen_bg-480x139.png 480w\" alt=\"push notifications tutorial\" width=\"650\" height=\"188\" \/><\/a><\/p>\n<p>Now your app will wake up in the background when it receives one of these push notifications.<\/p>\n<p>Inside\u00a0<code>AppDelegate<\/code>, replace\u00a0<code>application(_:didReceiveRemoteNotification:)<\/code>\u00a0with this more powerful version:<\/p>\n<pre class=\"language-swift hljs\" lang=\"swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">application<\/span><span class=\"hljs-params\">(\r\n  <span class=\"hljs-number\">_<\/span> application: UIApplication,\r\n  didReceiveRemoteNotification userInfo: [AnyHashable : Any],\r\n  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult)<\/span><\/span> -&gt; <span class=\"hljs-type\">Void<\/span>) {\r\n  \r\n  <span class=\"hljs-keyword\">let<\/span> aps = userInfo[<span class=\"hljs-string\">\"aps\"<\/span>] <span class=\"hljs-keyword\">as<\/span>! [<span class=\"hljs-type\">String<\/span>: <span class=\"hljs-type\">AnyObject<\/span>]\r\n  \r\n  <span class=\"hljs-comment\">\/\/ 1<\/span>\r\n  <span class=\"hljs-keyword\">if<\/span> aps[<span class=\"hljs-string\">\"content-available\"<\/span>] <span class=\"hljs-keyword\">as<\/span>? <span class=\"hljs-type\">Int<\/span> == <span class=\"hljs-number\">1<\/span> {\r\n    <span class=\"hljs-keyword\">let<\/span> podcastStore = <span class=\"hljs-type\">PodcastStore<\/span>.sharedStore\r\n    <span class=\"hljs-comment\">\/\/ Refresh Podcast<\/span>\r\n    <span class=\"hljs-comment\">\/\/ 2<\/span>\r\n    podcastStore.refreshItems { didLoadNewItems <span class=\"hljs-keyword\">in<\/span>\r\n      <span class=\"hljs-comment\">\/\/ 3<\/span>\r\n      completionHandler(didLoadNewItems ? .newData : .noData)\r\n    }\r\n  } <span class=\"hljs-keyword\">else<\/span>  {\r\n    <span class=\"hljs-comment\">\/\/ News<\/span>\r\n    <span class=\"hljs-comment\">\/\/ 4<\/span>\r\n    <span class=\"hljs-number\">_<\/span> = <span class=\"hljs-type\">NewsItem<\/span>.makeNewsItem(aps)\r\n    completionHandler(.newData)\r\n  }\r\n}\r\n<\/pre>\n<p>Let\u2019s go over the code:<\/p>\n<ol>\n<li>Check to see if\u00a0<code>content-available<\/code>\u00a0is set to 1, to see whether or not it is a silent notification.<\/li>\n<li>Refresh the podcast list, which is a network call and therefore asynchronous.<\/li>\n<li>When the list is refreshed, call the completion handler and lets the system know whether any new data was loaded.<\/li>\n<li>If it isn\u2019t a silent notification, assume it is news again and create a news item.<\/li>\n<\/ol>\n<p>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.<\/p>\n<p>That\u2019s all there is to it; to test it, push the following payload via\u00a0<em>Pusher<\/em>:<\/p>\n<pre class=\"language-json hljs\" lang=\"json\">{\r\n  <span class=\"hljs-attr\">\"aps\"<\/span>: {\r\n    <span class=\"hljs-attr\">\"content-available\"<\/span>: <span class=\"hljs-number\">1<\/span>\r\n  }\r\n}\r\n<\/pre>\n<p>If all goes well, nothing should happen! To see the code being run, change the scheme to \u201cWait for executable to be launched\u201d again and set a breakpoint within\u00a0<code>application(_:didReceiveRemoteNotification:fetchCompletionHandler:)<\/code>\u00a0to make sure it runs.<\/p>\n<p>&nbsp;<\/p>\n<p>source:\u00a0https:\/\/www.raywenderlich.com\/156966\/push-notifications-tutorial-getting-started<\/p>\n","protected":false},"excerpt":{"rendered":"<p>iOS developers love to imagine users of their awesome app using the app all day, every day. Unfortunately, the cold hard truth is that users will sometimes have to close the app and perform other activities. Laundry doesn\u2019t fold itself, you know :] Happily,\u00a0push notifications\u00a0allow developers to reach users and perform small tasks even when\u2026 <span class=\"read-more\"><a href=\"https:\/\/adriangrigoras.com\/blog\/push-notifications-tutorial-started\/\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30,31],"tags":[],"class_list":["post-862","post","type-post","status-publish","format-standard","hentry","category-architecture","category-programming"],"_links":{"self":[{"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/posts\/862","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/comments?post=862"}],"version-history":[{"count":1,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/posts\/862\/revisions"}],"predecessor-version":[{"id":863,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/posts\/862\/revisions\/863"}],"wp:attachment":[{"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/media?parent=862"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/categories?post=862"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/adriangrigoras.com\/blog\/wp-json\/wp\/v2\/tags?post=862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}