- PVSM.RU - https://www.pvsm.ru -
Конференция WWDC прошла, а докладов, которые стоит посмотреть, осталось ещё очень много. Были ключевые темы, которым Apple уделила особое внимание. Core ML, Siri Shortcuts и, конечно же, изменения в Notifications.

Так как не у всех найдётся достаточно свободного времени, чтобы пробираться через дебри документации, которая, как это обычно бывает, на стадии бета-тестирования оставляет желать лучшего, я подготовил обзор новых возможностей и подкрепил материал практической реализацией. Читйте, осознавайте и внедряйте в свои приложения.
Начнём с обзора возможностей, которые добавила Apple.
Для реализации ничего делать не нужно. iOS 12 автоматически сгруппирует сообщения за вас. Но есть нюансы, которые касаются кастомизации, локализации и группировки, но не на основе идентификатора приложения, а, например, в зависимости от имени пользователя, который отправил уведомление.
Кроме того, если вы будете тестировать группировку нотификаций, обратите внимание — они начнут собираться в пачку только в том случае, если единовременно все нотификации не могут поместиться на экране. Например, на экране iPhone 8 для этого нужно разместить 5 и более нотификаций.

Чтобы не перегружать этот материал, я вынес его в отдельный текст, который появится на Хабре в ближайшее время.
Следующий пункт — новые возможности для нотификаций и в частности класса NSExtensionContext. Он отвечает за взаимодействие с виджетами, Siri, проигрывание медиаконтента. Нас больше интересуют уведомления. Были добавлены два метода и одна переменная:
var notificationActions: [UNNotificationAction] { get set }
Переменная позволяет во время взаимодействия с уведомлением подменить набор доступных действий:
func dismissNotificationContentExtension()
func performNotificationDefaultAction()
Методы открывают приложение, либо скрывают уведомление.
Для демонстрации возможностей напишем небольшое приложение.
Сперва добавим в приложение отправку локальных уведомлений:
let actions = [
UNNotificationAction(identifier: "like-action", title: "Like", options: []),
UNNotificationAction(identifier: "open-app", title: "Open App", options: []),
UNNotificationAction(identifier: "dismiss", title: "Dismiss", options: []),
]
let simpleCategory = UNNotificationCategory(identifier: "category-simple", actions: actions, intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([simpleCategory])
Второй метод будет выполнять отправку локального уведомления через заданный промежуток времени для того, чтобы мы успели свернуть приложение или заблокировать телефон:
UNUserNotificationCenter.current().getNotificationSettings {
(settings) in
guard settings.authorizationStatus == .authorized else { return }
let content = UNMutableNotificationContent()
content.title = "Cat Title"
content.subtitle = "Cat Subtitle"
content.body = "Cat Body"
content.sound = .default
content.categoryIdentifier = "category-simple"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let uuid = UUID().uuidString
let request = UNNotificationRequest(identifier: uuid, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: {
(error) in
})
}
Далее необходимо добавить в приложение новый таргет Notification Content Extension. Он позволяет настроить параметры отображения уведомлений и обработку действий.

Будет создан plist-файл, ViewController и Storyboard:

В plist-файле нас интересуют следующие ключи:
В сториборд создаём UIImageView, UILabel для отображения заголовка уведомления и UIButton для взаимодействия с приложением.

Во View Controller'е создаём методы для открытия приложения и скрытия нотификации:
func openApp() {
extensionContext?.performNotificationDefaultAction()
}
func dismissNotification() {
extensionContext?.dismissNotificationContentExtension()
}
Реализуем методы протокола UNNotificationContentExtension.
Первый позволит отобразить необходимый текст:
func didReceive(_ notification: UNNotification) {
self.notificationTitleLabel.text = notification.request.content.body
}
Второй нужен для обработки действий от UNNotificationAction. В этом же методе выполняется подмена действий с помощью присвоения extensionContext?.notificationActions:
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
switch response.actionIdentifier {
case "like-action":
let actions = [
UNNotificationAction(identifier: "1-star", title: "★", options: []),
UNNotificationAction(identifier: "2-star", title: "★ ★", options: []),
UNNotificationAction(identifier: "3-star", title: "★ ★ ★", options: []),
]
extensionContext?.notificationActions = actions
case "open-app":
openApp()
default:
dismissNotification()
}
}
Обработка нажатий на сердце выполняется как обычно, через IBAction [1]:
@IBAction func defaultButtonTapped(_ sender: UIButton) {
openApp()
}

Запускаем приложение и смотрим, что у нас получилось:
Следующее нововведение позволяет добавить в настройки уведомлений новый пункт меню. При тапе на него будет осуществлён вызов метода, который вы можете реализовать в приложении. Например, пользователь может напрямую из системных настроек попасть в ваше приложение и в нём включить только те нотификации, которые он действительно хочет получать. Что нужно сделать для реализации?
Во-первых, при авторизации нотификаций добавляем ещё один параметр — providesAppNotificationSettings:
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound, .providesAppNotificationSettings])
Во-вторых, реализуем метод userNotificationCenter(_:openSettingsFor:) протокола UNUserNotificationCenterDelegate:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
openSettings()
}
func openSettings() {
let storyboard = UIStoryboard(name: "Settings", bundle: nil)
let settings = storyboard.instantiateViewController(withIdentifier: "Settings")
window?.rootViewController = settings
}
}

Пользователь не всегда понимает, хочет ли он получать уведомления от вашего приложения. Поэтому при первом запуске приложения такой выбор ему сделать сложно. С большой вероятностью он откажется от вашего предложения. Для таких ситуаций Apple предлагает использовать Provisional Authorization. При запросе авторизации на отправку нотификаций добавляется ещё один параметр — provisional. authorizationStatus для таких уведомлений, также приходит в приложение со статусом provisional.
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .provisional])
Пользователь при запуске приложения не получит запрос на авторизацию. Однако, чтобы его не беспокоить, приложение помещается в так называемый jail. Для уведомлений отключены звуки, бейджи; они отображаются только в Notification Center. На заблокированном экране или в виде баннеров они появляться не будут.
При получении уведомления у пользователя появляются две дополнительные кнопки. Одна полностью заблокирует уведомления или предложит перейти в настройки, а вторая переведёт ваши уведомления в статус авторизованных:

Последнее изменение добавляет ещё один тип уведомлений — Critical Alerts. Эти уведомления полностью игнорируют настройки вашего телефона, выключенный звук или режим «не беспокоить». Apple рекомендует использовать их в медицинских приложениях (например, у пользователя устройства резко подскочил уровень сахара), а также для обеспечения безопасности пользователей дома или в публичных местах.
Запрос на авторизацию содержит особый знак:

В настройках добавляется новый пункт, а на экране уведомлений отображается дополнительный значок:

Для отправки критических уведомлений придётся пройти процедуру валидации вашего приложения на сайте Apple [2].
Для использования уведомлений применяем параметр criticalAlert:
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .criticalAlert])
И формируем содержимое нотификации:
let content = UNMutableNotificationContent()
content.title = "WARNING"
content.body = "Storm alert"
content.categoryIdentifier = "storm-alert"
content.sound = UNNotificationSound.defaultCriticalSound(withAudioVolume: 1.0)
Для Critical Alerts можно указать громкость, с которой нотификация будет срабатывать независимо от настроек пользователя.
Надеюсь, этот материал упростит процесс внедрения новых уведомлений в ваше приложение. Если с какими-то этапами реализации не получилось разобраться, предлагаю ознакомиться с кодом на github [3] или задать вопрос в комментах.
Также можете посмотреть статью от e-Legion по уведомлениям для iOS 10 [4] или запись доклада с WWDC — What’s New in User Notifications. [5] Обсудить нововведения сможем на MBLT DEV 2018 [6] в Москве 28 сентября.
Хорошего дня и котиков всем ^_^
Автор: GxocT
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/285252
Ссылки в тексте:
[1] IBAction: https://habr.com/users/ibaction/
[2] сайте Apple: https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/
[3] github: https://github.com/GxocT/Catifications
[4] уведомлениям для iOS 10: https://habr.com/company/e-Legion/blog/303970/
[5] What’s New in User Notifications.: https://developer.apple.com/videos/play/wwdc2018/710/
[6] MBLT DEV 2018: https://mbltdev.ru/ru?utm_source=iOS12_notifications&utm_medium=habr
[7] Источник: https://habr.com/post/416307/?utm_campaign=416307
Нажмите здесь для печати.