Туториал: Push Notifications

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Xcode: 
Xcode 9 beta 4
Swift: 
Swift 4.0

Заметка

Данное учебное пособие предполагает, что вы используете хотя бы Xcode 8.3 и Swift 3.1 (Также проверено на Xcode 9 Beta 4 или более поздней версии, Swift 4 и iOS 11)

Разработчики iOS, любят представить свое хорошо разработанное приложение, которое понравится пользователям, желая, чтобы они использовали его в течение всего дня и каждый день. К сожалению разработчиков, суровая правда в том, что пользователи не могут работать с приложениями целый день и им приходиться время от времени закрывать свои приложения, чтобы заняться своими делами… ну или открыть другое приложение, ШУТКА :]

К счастью, такой инструмент, как push-уведомления, позволяет разработчикам “достучаться” до пользователей и выполнить небольшие задачи, при том, что приложение в этот момент закрыто и не используется!

Push-уведомления становятся все более и более мощным инструментом, с того времени, как они были впервые представлены. 

Возможности push-уведомления в iOS 10:

  • Отображение короткого текстового сообщения.
  • Воспроизведение короткого звукового уведомления.
  • Установить число/номер на иконке приложения.
  • Предоставить какие-либо действия, которые пользователь может предпринять, не открывая приложение.
  • Отобразить медиа вложения
  • Позволяет “бесшумно разбудить” приложение в фоновом режиме и выполнить задачу

Данное учебное пособие будет посвящено тому, как работать с push-уведомлениями и позволит вам поработать с ними.

Прежде чем приступить к работе, вам потребуется следующее:

  • Реальный iOS Девайс. Push-уведомления не работают в симуляторе, поэтому вам понадобится реальное устройство.
  • Требуется участие в программе Apple Developer Program. Начиная с Xcode 7, вы можете тестировать приложения на своем устройстве без участия в данной программе. Однако для настройки и отправки push-уведомлений вам все равно потребуется сертификат push-уведомлений для вашего App ID, для которого и требуется участие в программе.
  • Приложение Pusher. Вы будете использовать это приложение для отправки уведомлений на устройство. Для его установки следуйте инструкциям здесь.

Поехали!

Для отправки и получения push-уведомления необходимо выполнить три основные задачи:

  • Приложение должно быть правильно настроено и зарегистрировано в Apple Push Notification Service (APNS) для получения push-уведомлений при каждом запуске.
  • Сервер должен передавать push-уведомление в APNS, и затем уже APNS будет отправлять эти уведомления на одно или несколько определенных девайсов.
  • Затем приложение должно получить push-уведомление, после этого приложение выполняет какие либо задачи или обрабатывает действия пользователя (например открыть приложение из пришедшего уведомления), используя обратный вызов в делегате приложения.

Отправка push-уведомлений является обязанностью сервер-компонентного приложения и  как правило от одного приложения к другому оно реализуется по-разному. Многие приложения используют сторонние решения (здесь вы можете найти несколько хороших примеров) для отправки push-уведомлений, в то время как другие используют свои собственные решения и/или популярные библиотеки (например, Houston).

Для начала загрузите стартовый проект WenderCast.

WenderCast - будет нашим приложением для поиска подкастов на raywenderlich.com и новых новостей.

Откройте WenderCast.xcodeproj в Xcode и давайте взглянем на него. Запустите проект в симуляторе iPhone, чтобы увидеть последние подкасты (в скором времени нам понадобится использовать реальное устройство!):

Вы должны увидеть следующее:

На данный момент проблема приложения заключается в том, что он не дает знать пользователям, когда доступен новый подкаст. У него также нет новостей. Все это вы скоро сможете исправить с помощью ваших push-уведомлений! :]

Настройка Push-уведомления в вашем приложении

Push-уведомления нуждаются в хорошей безопасности. Это очень важно, вы же не хотели бы, чтобы кто-то другой отправлял push-уведомления пользователям в вашем приложении. К сожалению, но скажу, что это означает, что есть несколько необходимых операций для настройки push-уведомлений приложения.

Включение службы Push-уведомления

Первым делом нужно изменить идентификатор приложения (App ID). Перейдите в App Settings (Настройки приложения) -> General и измените в поле Bundle Identifier на какой нибудь уникальный идентификатор: 

Чуть ниже, в разделе Signing измените поле Team, выберите в списке вместо надписи None,  другого разработчика, в нашем случае скорее мы говорим конкретно про вашу учетную запись, с которой мы будем работать. Опять же повторюсь, это должен быть оплаченный аккаунт разработчика. Если вы не видите в списке Team своей учетной записи или других каких-либо записей разработчиков, которые может с вами поделились аккаунтом :] , то сначала вам придется добавить свою учетную запись (development team), сделать это можно, через Xcode -> Preferences -> Accounts -> +.

Затем вам нужно создать идентификатор приложения (App ID) в учетной записи разработчика (developer account), на котором будет отображено разрешение на использование push-уведомлений. К счастью, у Xcode есть простой способ, чтобы сделать это. Перейдите в App Settings (Настройки приложения) -> Capabilities и переключите переключатель для Push Notifications (Push-уведомлений) в положение On (Вкл.).

После загрузки он должен выглядеть следующим образом: 

Если возникнут какие-либо проблемы, посетите Apple Developer Center. Там вам останется только согласиться с новой лицензией разработчика, которую Apple так любит часто менять ;] и затем повторите попытку. В худшем случае вам потребуется вручную добавить права на push-уведомления с помощью двух кнопок + и Edit.

За кадром это создаст идентификатор приложения (App ID), а затем добавит права на push-уведомления. Вы можете посетить Apple Developer Center и убедиться в этом: 

По настройкам это все, что нам потребовалось.

Регистрация Push-уведомлений

Регистрация push-уведомлений происходит в 2 шага. Сначала, вы должны получить разрешение от пользователя на отображение любого вида уведомлений, после которого приложение сможет зарегистрироваться для получения удаленных уведомлений, отправленных вашим сервером. Если все пойдет хорошо, система предоставит вам токен устройства/девайса (device token), который вы можете представить как «адрес» этого устройства/девайса.

В WenderCast мы регистрируем push-уведомления сразу после запуска приложения.

Откройте AppDelegate.swift и добавьте фрэймворк в верхнюю часть файла:

import UserNotifications

Затем добавьте следующий метод в конце AppDelegate:

func registerForPushNotifications() {
  UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
    (granted, error) in
    print("Permission granted: \(granted)")
  }
}

И теперь добавьте вызов этого метода registerForPushNotifications() в конце метода application(_:didFinishLaunchingWithOptions:), как показано ниже:

func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  // ... existing code ...
  registerForPushNotifications()
  return true
}

Идем дальше: UNUserNotificationCenter был представлен в iOS 10 в фрэймворке UserNotifications. Он отвечает за управления всего того, что связанно с действиями уведомлений в приложении.

У UNUserNotificationCenter.current() вы вызываете requestAuthorization(options: completHandler :), чтобы (как вы догадались) запросить авторизацию для push-уведомлений. Здесь вы должны указать тип уведомлений, которое будет использовать ваше приложение. Эти типы (представленные как UNAuthorizationOptions) может представлять собой любую комбинацию из следующего:

  • .badge позволяет приложению отобразить число в углу иконки приложения.
  • .sound позволяет приложению воспроизвести звук.
  • .alert позволяет приложению отобразить текст.
  • .carPlay позволяет приложению отобразить уведомления в системе CarPlay.

Вы вызываете метод registerForPushNotifications в application(_:didFinishLaunchingWithOptions:), чтобы наше демо приложение попыталось зарегистрироваться для получения push-уведомлений и получало их в любое время.

Теперь соберите (Build) и запустите (Run) ваше приложение. Когда приложение запустилось, вы должны увидеть окошко, которое будет запрашивать разрешение на отправку уведомлений.

Нажмите ОК и ТА-ДАААМ! Приложение теперь может отображать уведомления. Отлично! Но что если…? Что делать, если пользователь отказал в разрешении на отправку уведомлений? Для этого давайте добавим следующий метод, внутри AppDelegate:

func getNotificationSettings() {
  UNUserNotificationCenter.current().getNotificationSettings { (settings) in
    print("Notification settings: \(settings)")
  }
}

Этот метод отличается от нашего предыдущего метода registerForPushNotifications. В предыдущем методе вы указали ваши собственные параметры, в то время как этот метод возвращает параметры, которые были предоставлены самим пользователем.

Очень важно, чтобы метод getNotificationSettings(completeHandler :) был вызван внутри замыкания в методе requestAuthorization, который вызывается каждый раз, когда приложение завершает запуск. 

Это нужно для того, чтобы в любое время пользователь мог перейти, в раздел «Настройки» в вашем приложении и изменить разрешение для уведомлений.

Давайте реализуем это, добавьте метод getNotificationSettings() в замыкание метода requestAuthorization, как показано ниже:

func registerForPushNotifications() {
  UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
    (granted, error) in
    print("Permission granted: \(granted)")

    guard granted else { return }
    self.getNotificationSettings()
  }
}

Первый шаг завершен, и теперь наше приложение готово зарегистрироваться для удаленных уведомлений!

Обновите метод getNotificationSettings() следующим образом:

func getNotificationSettings() {
  UNUserNotificationCenter.current().getNotificationSettings { (settings) in
    print("Notification settings: \(settings)")
    guard settings.authorizationStatus == .authorized else { return }
    UIApplication.shared.registerForRemoteNotifications()
  }
}

Здесь вы проверяете, что authorizationStatus равен .authorized, то есть пользователь предоставил разрешение для уведомлений и если это выражение верное (то есть возвращает TRUE), то вы вызываете UIApplication.shared.registerForRemoteNotifications().

Добавьте следующие два метода в конце AppDelegate; Они будут вызваны для того, чтобы сообщить вам о результате registerForRemoteNotifications:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  let tokenParts = deviceToken.map { data -> String in
    return 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)")
}

Как видно из названия, система вызывает application(_:didRegisterForRemoteNotificationsWithDeviceToken:), если регистрация прошла успешно, или вызывает application(_:didFailToRegisterForRemoteNotificationsWithError:) в случае ошибки.

Текущая реализация application(_:didRegisterForRemoteNotificationsWithDeviceToken:) выглядит непонятно, но она просто принимает deviceToken и преобразует ее в строку.

То есть полученный наш токен девайса (deviceToken) является результатом этого процесса. Пришедший токен, это то, что присылает нам APNS, который в свою очередь уникально идентифицирует ваше приложение на определенном девайсе. При отправке push-уведомлений, приложение использует токены девайса как «адреса», чтобы доставить уведомления исключительно приложению на вашем девайсе. 

Заметка

Существует несколько причин, по которым регистрация может завершиться неудачей. В большинстве случаев это связано с тем, что приложение работает на симуляторе или потому, что конфигурация идентификатора приложения не была выполнена должным образом. Сообщение об ошибке обычно всегда информативное и указывает на ошибку, которую вы допустили.

Вот он наш токен! Запустите ваше приложение. Убедитесь, что вы работаете на реальном девайсе/устройстве, затем вы должны получить токен девайса/устройства в окошке консоли. Вот как выглядит мой токен: 

Скопируйте  и сохраните этот токен в любом месте, где вам удобно.

Перед тем, как вы уже захотите отправить push-уведомление, вам потребуется совершить еще несколько настроек, поэтому давайте перейдем в Apple Developer Member Center и войдем в систему.

Создание SSL-сертификата и файла PEM

В вашем центре участников (member center) перейдите в Certificates

IDs & Profiles -> Identifiers -> App IDs и выберите идентификатор приложения (App ID) для вашего приложения. В разделе Application Service, Push-уведомления должны отображаться как Configurable (Настраиваемые): 

Нажмите Edit (Изменить) и прокрутите вниз до Push Notifications

В Development SSL Certificate нажмите Create Certificate… и затем выполните шаги для создания CSR (Certificate Signing Request). После того, как у вас уже есть CSR, нажмите continue и следуйте инструкциям для создания (Generate) сертификата с помощью CSR. В завершении загрузите сертификат и кликните на него два раза, в итоге это должно добавить его в цепочку ключей (Keychain), в паре с приватным ключом: 

Вернувшись в центр участника (member center), ваш идентификатор приложения (App ID), должен теперь отображать push-уведомления в Enabled  это говорит о том, что у вас появилась возможность сделать ваше приложения с push-уведомлениями: 

Фух! Было затрачено не мало времени, но согласитесь, это того стоило :] - с новым файлом вашего сертификата, вы можете отправить свое первое push-уведомление!

Отправка Push-уведомлений

Отправка push-уведомлений возможна при помощи сгенерированного SSL-сертификата, которая дает право вашему серверу защищенно соединиться с APNS, и мы это сможем сделать с помощью только что созданного нашего push-сертификата… и с помощью приложения Pusher.

Запустите Pusher. Приложение автоматически проверит наличие push сертификатов в цепочке ключей (Keychain) и отобразит их в раскрывшемся списке. Выполните следующее:

  • Выберите ваш push сертификат из раскрывшегося списка.
  • Вставьте токен вашего девайса (device token) в поле Device push token.
  • Измените функциональую часть на:
{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://raywenderlich.com"
  }
}
  • На девайсе, на котором ранее вы уже запускали приложение WenderCast, переведите его в фон или заблокируйте, иначе push-уведомление работать не будет *
  • Нажмите на кнопку Push в приложении Pusher 

Вы должны увидеть свое первое push-уведомление: 

Заметка

Вы ничего не увидите, если приложение будет открыто и работать на переднем плане. Push-уведомление будет доставлено, но в этот момент в приложении ничего нет, для того, чтобы обработать его. Поэтому просто закройте приложение и попробуйте отправить уведомление еще раз.

Часто возникающие вопросы

Есть несколько проблем, которые у вас могут возникнуть:

У вас получилось так, что некоторые уведомления получены, но не все: если вы отправили несколько push-уведомлений одновременно и получили не все, не переживайте! Это сделано специально. APNS обеспечивает очередь QoS (Quality of Service) для каждого девайса поддерживающее push-уведомления в приложении. Размер этой очереди равен 1, поэтому, если вы отправляете несколько уведомлений, то последнее уведомление переопределяет предыдущее.

Проблема с подключением к службе Push Notification: одна из таких проблем может заключаться в том, что существует брандмауэр, который блокирует порты, используемые APNS. Убедитесь, что эти порты у вас разблокированы. Еще проблема может быть в том, что приватный ключ и файл CSR неверны. Помните, что каждый идентификатор приложения (App ID) имеет уникальную комбинацию CSR и приватного ключа (private key).

Анатомия базового Push Notification

Прежде чем мы перейдем к 3 задаче, посмотрите внимательно на push-уведомление, на его функциональную часть:

{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://raywenderlich.com"
  }
}

Для тех кто не знаком с JSON, у него есть блок, который ограниченный фигурными скобками { }, и содержит в себе словарь, который состоит из пары ключ-значение (key/value) (все точно так же, как и Dictionary в Swift).

Функциональная часть уведомления - это словарь, который содержит в себе хотя бы один элемент (то есть ключ-значение), aps, значение которой тоже является словарем. В указанном выше примере aps содержит в себе поля: alert, sound и link_url. Когда наше push-уведомление получено девайсом, оно отобразит всплывающее окошко с текстом «Breaking News!» и воспроизведет стандартный звуковой сигнал.

link_url - это обычное поле. Вы можете настроить по-другому, добавив и другие поля в функциональную часть уведомления, как это сделали мы, и они будут доставлены в ваше приложение. Поскольку мы еще не обрабатываем наше уведомление в приложении, эта пара “ключ-значение” в настоящий момент ничего не делает.

Есть шесть ключей, которые вы сможете добавить в словарь aps:

  • alert. Это может быть строка, как и в предыдущем примере, или словарь. В качестве словаря он может отобразить текст или изменить другие аспекты уведомления.
  • badge. Это номер, который будет отображаться в углу иконки приложения. Вы можете удалить номер, задав его номер равным 0.
  • thread-id. Вы можете использовать этот ключ для группировки уведомлений.
  • sound. Установив этот ключ, вы можете воспроизвести свои собственные звуковые уведомления, которые есть в приложении, вместо звукового уведомления по умолчанию. Данные звуковые уведомления должны быть не более 30 секунд и иметь несколько ограничений.
  • content-available. Установив этот ключ на 1, push-уведомление будет тихим. Мы это рассмотрим позже в рамках данного обучение по push-уведомлениям.
  • category. Это определяет категорию уведомления, которое используется для отображения пользовательских действий в уведомлении.Это тоже будет рассмотрено в ближайшее время.

Кроме этого вы можете добавить столько пользовательских данных, сколько хотите, если полезная нагрузка (payload) не превышает максимальный размер 4096 байт.

После того, как вам удалось сделать отправку push-уведомления на ваш девайс, давайте перейдите к следующему разделу. :]

Обработка Push Notifications

Далее по тексту определения:

Action (действие) — в переводе с английского «действие». В нашем случае это означает, что если пришло уведомление, то мы можем произвести с ним какие-либо экшены (действия), например, нажать на пришедшем уведомлении кнопку и по нажатию на кнопку, должно произойти какое-либо действие, открытие приложения или отправка нашего Да/Нет, если они есть на кнопках.

Custom (пользовательский) — в переводе с английского изготовленный на заказ. В нашем случае это означает, что мы сами создаем, настраиваем/конфигурируем наш метод или что-либо еще по своему “вкусу”.

В этом разделе вы узнаете, как работать с определенными действиями в своем приложении, когда приходят push-уведомления и/или когда пользователи нажимают на них.

Что происходит, когда вы получаете Push-уведомление?

Когда ваше приложение получает push-уведомление, вызывается опредленный метод в UIApplicationDelegate.

Уведомление могут обрабатываться по-разному, все зависит от того, в каком сейчас состоянии (закрыто или в фоновом режиме) находится ваше приложение, и какого вида пришло полученное уведомление соответсвенно:

  • Если ваше приложение не запущено и пользователь запускает его, нажав на push-уведомление. Push-уведомление передается вашему приложению в launchOptions в методе application(_:didFinishLaunchingWithOptions:).
  • Если ваше приложение запущено и находится на переднем плане, или в фоновом режиме, будет вызван метод application(_:didReceiveRemoteNotification:fetchCompletionHandler:). Если пользователь открывает приложение, нажав на push-уведомление, этот метод будет вызван еще раз, так вы сможете обновить интерфейс (UI) и отобразить актуальную информацию.

В первом случае WenderCast создаст элемент (ячейку) новостей и откроется непосредственно в разделе новостей. Добавьте следующий код в конце метода application(_:didFinishLaunchingWithOptions:) перед оператором return true:

// Check if launched from notification
// 1
if let notification = launchOptions?[.remoteNotification] as? [String: AnyObject] {
  // 2
  let aps = notification["aps"] as! [String: AnyObject]
  _ = NewsItem.makeNewsItem(aps)
  // 3
  (window?.rootViewController as? UITabBarController)?.selectedIndex = 1
}

Этот код выполняет три функции:

  1. Он проверяет, существует ли значение для UIApplicationLaunchOptionsKey.remoteNotification в launchOptions, и если оно существует, то мы получаем доступ к функциональной части push-уведомления, которое вы отправили.
  2. Если значение, которое мы получили выше существует, то вы перехватываете словарь aps (notification["aps"]) и передаете его в создание NewNewsItem(_ :), который представляет собой вспомогательный метод, предоставляемый для создания NewsItem (новой ячейки) из словаря и производит обновление таблицы новостей.
  3. И наконец, он изменяет выбранную вкладку контроллера на вкладку 1, это раздел новостей.

Чтобы проверить это, вам необходимо отредактировать схему (Edit Scheme) WenderCast

В разделе Run -> Info выберите Wait for executable to be launched:

Этот параметр заставит отладчика (debugger) ждать приложение, которое будет запущено в первый раз после установки, чтобы присоединиться к нему.

Запустите ваше приложение. Как только сделаете это, отправьте еще раз свеженькие новости (push-уведомления). Нажмите на уведомление, и приложение должно открыть вашу отправленную новость:

Заметка

Если вы перестанете получать push-уведомления, вероятно, токен вашего девайся изменился. Это может произойти, если вы удалили или переустановили приложение. Дважды проверьте токен девайса, чтобы точно быть уверенным.

Давайте разберем другой случай, добавьте следующий метод в AppDelegate

func application(
  _ application: UIApplication,
  didReceiveRemoteNotification userInfo: [AnyHashable : Any],
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  
  let aps = userInfo["aps"] as! [String: AnyObject]
  _ = NewsItem.makeNewsItem(aps)
}

Этот метод напрямую использует вспомогательную функцию для создания нового NewsItem (новую ячейку). Сейчас вы можете обратно изменить схему для запуска приложения на опцию automatically (автоматически), если конечно вам это хочется.

Запустите ваше приложение. Держите приложение открытым на переднем плане и чтобы оно было в разделе news (Новости). Отправьте еще одно сообщение с уведомлением о новостях и посмотрите, как это волшебным образом оно появится feed:

feed - это новости, точнее новостная лента, в которой отображаются все действия, новые видео, которые недавно залил пользователь, текст, который он написал, кому поставил лайк, что добавил в плейлист и т.д... 

Вот оно! Теперь и таким способом ваше приложение может обрабатывать последние новости.

Важно следующее: push-уведомления могут много раз остаться пропущенными. Это нормально для данного приложения WenderCast, поскольку весь список новостей здесь не самое главное, но в целом вы не должны использовать push-уведомления, как единственный способ доставки контента.

Вместо этого push-уведомления должны сигнализировать о наличии нового контента и позволить приложению загружать контент из определенного источника (например, из REST API). В этом плане приложение WenderCast немного ограничено, так как оно не имеет настоящего созданного серверного компонента.

Уведомления с действиями

Уведомления с действиями позволяют добавить свои собственные кастомные кнопки на самом уведомлении. Возможно раньше вы замечали это по уведомлениям, которые приходят по электронной почте или твитам, которые позволяют вам тут же дать ответ нажав «ответить» или отметить как «любимое».

Уведомления с действиями определяются внутри вашего приложением, когда вы регистрируетесь для получения уведомлений с помощью categories. Каждая категория (categories) уведомлений может иметь несколько предустановленных кастомных экшенов (действий).

После регистрации, в вашем сервере можно установить категорию (categories) для push-уведомлений.

Действия уведомлений будут доступны пользователю при его получении.

Для приложения WenderCast мы определили категорию «News» (Новости) с нашими действиями под названием «View», которое позволяет пользователям напрямую, по желанию просматривать в приложении новостные статьи.

Итак, заменим registerForPushNotifications() в AppDelegate следующим образом:

func registerForPushNotifications() {
  UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
    (granted, error) in      
    print("Permission granted: \(granted)")
    
    guard granted else { return }
    // 1
    let viewAction = UNNotificationAction(identifier: viewActionIdentifier,
                                          title: "View",
                                          options: [.foreground])
    
    // 2
    let newsCategory = UNNotificationCategory(identifier: newsCategoryIdentifier,
                                              actions: [viewAction],
                                              intentIdentifiers: [],
                                              options: [])
    // 3
    UNUserNotificationCenter.current().setNotificationCategories([newsCategory])
    
    self.getNotificationSettings()
  }
}

Что наш новый код делает :

  1. Здесь вы создаете новое уведомление с кнопкой под названием «View», которое будет открывать приложение на передний плане при его запуске. Наше созданный экшн имеет уникальный идентификатор, который используется для отличия между другими действиями на этом же уведомлении.
  2. Затем вы определяете категорию новостей, которая содержит отображение действий (let viewAction). Он также имеет свой идентификатор, который должна содержать функциональная часть уведомления, чтобы указать, что push-уведомление относится именно к этой категории.
  3. И наконец, вызывая setNotificationCategories(_ :), вы регистрируете новое уведомление, соответствующее действию.

Запустите ваше приложение, чтобы зарегистрировать наши новые уведомления.

Переведите ваше приложение в фоновый режим, а затем отправьте уведомление со следующим содержимым через приложение Pusher:

{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://raywenderlich.com",
    "category": "NEWS_CATEGORY"
  }
}

Если все прошло хорошо, то вы сможете увидеть уведомление, и открыть приложение, через нажатие на экшн кнопку «View»

Отлично! Нажатие на эту кнопку запустит приложение WenderCast, но ничего не отобразит. Чтобы отобразить эту новость, вам нужно реализовать еще один обработчик событий в нашем делегате.

Обработка действий уведомлений

Каждый раз, когда срабатывает действие на уведомлении, UNUserNotificationCenter отправляет сообщение делегату. Давайте вернемся в AppDelegate.swift, и добавим следующее расширение в конце файла AppDelegate.swift:

extension AppDelegate: UNUserNotificationCenterDelegate { 
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
    // 1
    let userInfo = response.notification.request.content.userInfo
    let aps = userInfo["aps"] as! [String: AnyObject]
    
    // 2
    if let newsItem = NewsItem.makeNewsItem(aps) {
      (window?.rootViewController as? UITabBarController)?.selectedIndex = 1
      
      // 3
      if response.actionIdentifier == viewActionIdentifier,
        let url = URL(string: newsItem.link) {
        let safari = SFSafariViewController(url: url)
        window?.rootViewController?.present(safari, animated: true, completion: nil)
      }
    }
    
    // 4
    completionHandler()
  }
}

Здесь представлен callback, который вы получаете, когда приложение запускается, через пользовательские действия. Может показаться, что тут происходит много чего, но на самом деле это не так:

  1. Вы получаете aps словарь.
  2. Далее создаёте NewsItem (новую ячейку) из словаря и переходите в раздел «News».
  3. Проверяете идентификатор действия, который передается как identifier (actionIdentifier). Если это действие «View»  и ссылка является корректным URL-адресом, она отобразит ссылку в SFSafariViewController.
  4. И в завершении вызывается завершающий обработчик (completionHandler()), который передается нам системой после обработки экшена.

Есть еще один момент: вы должны установить делегат на UNUserNotificationCenter. Добавьте эту строку в начале метода application(_:didFinishLaunchingWithOptions:):

UNUserNotificationCenter.current().delegate = self

Запустите ваше приложение. Закройте приложение еще раз и давайте уже отправим другое уведомление о новостях со следующей функциональной частью:

{
  "aps": {
    "alert": "New Posts!",
    "sound": "default",
    "link_url": "https://raywenderlich.com",
    "category": "NEWS_CATEGORY"
  }
}

Нажмите на экшн, и вы увидите, что приложение WenderCast откроется в Safari View Controller сразу после запуска приложения:

Поздравляю, вы только что реализовали экшн уведомление! Отправьте еще несколько и попробуйте открыть уведомление разными способами, чтобы подробнее увидеть, как оно работает.

Тихие Push-уведомления

Тихие push-уведомления могут “разбудить” ваше приложение “бесшумно”, то есть запустить приложение в фоновом режиме и выполнить некоторые задачи. Например приложение WenderCast может использовать эту функцию для “тихого” обновления списка подкастов.

Как вы можете себе представить, в паре с вашим серверным компонентом это может быть даже очень эффективно. Вашему приложению не нужно будет постоянно запрашивать данные - вы можете сами отправить приложению “тихое” push-уведомление, если у вас появились новые данные.

Для начала перейдите в App Settings -> Capabilites и включите Background Modes (фоновый режим) для приложения WenderCast. И проверьте последнюю опцию, Remote Notifications:

Теперь ваше приложение будет “просыпаться” в фоновом режиме, когда оно получит одно из этих push-уведомлений.

В AppDelegate, замените application(_:didReceiveRemoteNotification:), на следующее:

func application(
  _ application: UIApplication,
  didReceiveRemoteNotification userInfo: [AnyHashable : Any],
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  
  let aps = userInfo["aps"] as! [String: AnyObject]
  
  // 1
  if aps["content-available"] as? Int == 1 {
    let podcastStore = PodcastStore.sharedStore
    // Refresh Podcast
    // 2
    podcastStore.refreshItems { didLoadNewItems in
      // 3
      completionHandler(didLoadNewItems ? .newData : .noData)
    }
  } else  {
    // News
    // 4
    _ = NewsItem.makeNewsItem(aps)
    completionHandler(.newData)
  }
}

Итак, давайте рассмотрим:

  1. Проверьте, установлено ли значение content-available равное 1, чтобы мы понимали, является ли уведомление “тихим”.
  2. Здесь обновляется список подкастов, который представляет собой сетевой запрос из этого следует то, что он работает асинхронно.
  3. Когда список обновился, вызывается завершающий обработчик (completionHandler) и сообщает системе, были ли загружены новые данные.
  4. Если к нам пришло не тихое уведомление (предположим, что так и случилось), то тогда просто создаем еще один новостной элемент (ячейку).

Обязательно вызывайте завершающий обработчик (completionHandler) в любом из полученных результатов (в нашем примере это либо шаг 3 или шаг 4). Система следит за потреблением энергии и временем, которое ваше приложение использует в фоновом режиме, и при необходимости приложение может быть просто закрыто.

Вот и все. Чтобы проверить “бесшумные” уведомления, передайте следующую функциональную часть (payload) через приложение Pusher:

{
  "aps": {
    "content-available": 1
  }
}

Если все прошло успешно, ничего не должно произойти, то есть вы ни чего не увидите! Чтобы увидеть, как выполняется код, вы можете заново изменить схему на “Wait for executable to be launched” и установите контрольную точку в application(_:didReceiveRemoteNotification:fetchCompletionHandler:), чтобы убедиться, что она запущена.

Поздравляю! Вы завершили данный урок по push-уведомлениям и сделали WenderCast полнофункциональным приложением с push-уведомлениями! Конечный проект вы можете скачать здесь. Помните, что вам все равно необходимо изменить идентификатор пакета (bundle ID) и создавать сертификаты, чтобы все заработало (как мы это сделали в разделе Enabling the Push Notification Service).

Несмотря на то, что в наши дни push-уведомления являются одним из важных моментов в приложениях, пользователям довольно часто отклоняют разрешения для них, если ваши уведомления будут приходить слишком часто. Но с продуманным дизайном, push-уведомления все же помогают завлечь пользователей в ваше приложение снова и снова! :]

А что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Просвиряков Вадим

Источник урока.