Полное руководство по расширенным push-уведомлениям с видео, изображениями, GIF и многим другим

Перевод статьи

Локальные уведомления, Push-уведомления. Триггеры местоположения. Мультимедийные сообщения, дополненные изображениями, видео, гифками или полностью пользовательским пользовательским интерфейсом. Изменения iOS15

Введение

Почти каждому приложению нужен способ общения с пользователями. Одна из основных целей маркетинговой команды — донести до большего числа пользователей актуальную информацию, взаимодействовать с ними и помочь понять ценность приложения. Одним из их любимых вариантов являются push-уведомления.


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


Прежде чем мы углубимся в детали, вот текстовая игра, над которой я работал давным-давно, в которой активно используются уведомления. Он поддерживается на iPhone, но на Apple Watch можно получить больше удовольствия. Линия жизни: Белая мгла

В этой статье будут рассмотрены несколько тем, таких как: 

  • Локальные уведомления
  • Всплывающие уведомления
  • Тихие push-сообщения
  • Как добавить кнопки действий
  • Как настроить его в Xcode
  • Как зарегистрироваться
  • Типы сообщений и состояния
  • iOS 15 изменения
  • APNs
  • Как настроить сертификат
  • Rich Media (мультимедийные сообщения с изображением, GIF или видео),Расширение службы уведомлений
  • Rich Media (сообщения с пользовательским содержимым),Расширение содержания уведомления
  • Пример того, как представить MapKit
  • Как справиться с подсчетом значков
  • Как тестировать на симуляторе и на устройстве
  • Полезные инструменты
  • Хорошие сторонние сервисы push-уведомлений 

Подпишитесь на уведомления

Независимо от того, выбран тип локального или push-уведомления, первым делом нужно попросить пользователя разрешить ему получать уведомления. Зарегистрироваться, чтобы иметь возможность получать уведомления. Много раз он вызывается из приложения (_:didFinishLaunchingWithOptions:), но гораздо более аккуратный вариант — вызывать его из пользовательского экрана, чтобы объяснить пользователю, почему мы будем использовать локальные и/или push-уведомления и их преимущества.

Когда мы вызываем requestAuthorization(options:), ОС представит представление Alert, которое можно увидеть на предыдущем изображении. Красным выделено имя приложения. Зеленая основа — это запрошенные параметры.


На изображении ниже показан один из примеров того, как пользовательский экран объясняет разрешения. Следует отметить, что когда ОС представляет предупреждение, приложение перейдет в фоновый режим, и после того, как пользователь ответит на вопрос разрешения ОС, оно вернется к приложению. Это означает, что он вызовет методы жизненного цикла AppDelegate/SceneDelegate.

Первым шагом является импорт UserNotifications.Далее нужно создать конфигурацию, которую мы хотели бы использовать, например, let options:UNAuthorizationOptions = [.alert, .sound, .badge].

func requestNotificationAuthorization() {
    
    let nc = UNUserNotificationCenter.current()
    let options: UNAuthorizationOptions = [.alert, .sound, .badge]
    
    nc.requestAuthorization(options: options) { granted, _ in
        print("\(#function) Permission granted: \(granted)")
        guard granted else { return }
    }
}

Это оповещение, звук и значок будут охватывать большинство потребностей приложения, но есть и другие параметры, такие как carPlay, CriticalAlert или, возможно, предварительные и т. д. 

  • alert — Чтобы получить всплывающее уведомление,
  • sound — чтобы включить звук, по умолчанию используется UNNotificationSound.default.
  • badge — Включить использование «красной точки» с номером на значке приложения.
  • carPlay — Чтобы иметь возможность отображать уведомления в CarPlay (ссылка на действительно хороший учебник по использованию CarPlay от друга)
  • CriticalAlert — предупреждение о критической ситуации проходит через «Не беспокоить». Должен быть разрешен специальным правом Apple.
  • предварительный — с этой опцией вам не нужно спрашивать разрешения у пользователя. Вы можете отправлять уведомления прямо сейчас. Но только в Центр уведомлений

 Если вы хотите проверить, какие параметры разрешены в приложении для пользователя, вы можете сделать это, используя следующий код:

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

Предварительные сообщения

Как уже упоминалось, это тихие сообщения, на которые не обращают внимания, и пользователь может получить их при установке приложения. Не нужно спрашивать разрешения. Вам все еще нужно вызвать метод requestAuthorization(options:). Но это не вызовет никаких вопросов у пользователя.

let options: UNAuthorizationOptions = [.provisional]

На предыдущем изображении вы можете увидеть, как пользователь видит сообщение. Пользователь по-прежнему может настроить его в настройках по своему усмотрению.

Локальные уведомления

Этот вид уведомления, как уже говорит нам его название, создается локально, в автономном режиме. Локальные уведомления могут быть запланированы по временному интервалу в качестве напоминания, которое будет запускать их по точному времени дня как событие или по географическому положению, геозоне. Примеры: 

  • Если вы хотите уведомить пользователя прекратить играть в World of Warcraft и проверить мясо, которое готовилось в течение 20 минут, иначе оно сгорит (интервал времени).
  • Что сегодня 1 февраля день рождения твоего лучшего друга (Календарь).
  • Что вы проходите мимо любимого магазина и вам нужно за молоком (местоположение)
  • Загрузка файла завершена в фоновом режиме (Интервал времени/Мгновенно)

Чтобы создать локальные уведомления, вам нужно запросить у пользователя разрешение, как в предыдущем объяснении.


Когда вы создаете локальное уведомление, ОС берет на себя ответственность. Когда ваше приложение убито или находится в фоновом режиме, ОС позаботится о том, чтобы оно было представлено пользователю при необходимости.


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


С технической точки зрения каждое локальное уведомление состоит из трех частей:

Содержание

Это визуальные и конфигурационные свойства уведомления. 

  • title — Простая строка, заголовок сообщения
  • subtitle — Простая строка, подзаголовок сообщения. Обычно это опционально используется маркетинговой командой.
  • body — простая строка, подзаголовок сообщения. Содержание сообщения
  • sound — UNNotificationSound.default используется по умолчанию. Вы можете использовать пользовательский
  • badge — номер на «Красной точке». Вы можете использовать это, чтобы установить точное число. Но вы можете иметь свою собственную систему, которая будет прибавлять или вычитать счет в зависимости от ваших потребностей. Вы можете комбинировать его с вашими входящими сообщениями, какой-либо системой обмена сообщениями для конкретного приложения. Что бы вы ни поддерживали.
  • user info — вы можете передать некоторые данные в виде пары ключ:значение.
  • attachments — вы можете использовать мультимедийные файлы, хранящиеся в вашем телефоне. По сути, вы можете иметь какое-то видео, изображение или gif в комплекте приложения и использовать его в качестве ресурса. Я не использовал это, так как Rich Media был гораздо лучшим вариантом, поскольку ресурсы загружаются. Позже это будет объяснено.
  • categoryIdentifier — это интересный вариант. Вы можете использовать это, чтобы добавить кнопки действий к уведомлению или различать мультимедийные файлы (расширение службы уведомлений или ваши действия расширения содержимого уведомлений).
  • interruptionLevel — это привнесла iOS 15. Это сообщает системе, насколько важно сообщение и когда его отправить.
  • active — система немедленно представляет уведомление, загорается экран и может воспроизводить звук.
  • critical — система немедленно представляет уведомление, включает экран и отключает звук, чтобы воспроизвести звук.
  • passive — система добавляет уведомление в список уведомлений без подсветки экрана и воспроизведения звука.
  • timeSensitive — система немедленно представляет уведомление, загорается экран и может воспроизводить звук, но не прерывает элементы управления системными уведомлениями.
  • threadIdentifier — вы можете назначить каждому локальному или push-уведомлению идентификатор, чтобы сгруппировать их визуально в Центре уведомлений. Например, все информационные сообщения должны быть сгруппированы.

Триггер

Это тип триггера, который разбудит ОС и отправит уведомление.

  • time — timeInterval, время, которое пройдет до срабатывания уведомления
  • calendar — точная дата-время, когда нужно запустить уведомление
  • location — когда вы входите или выходите из определенного региона, срабатывает уведомление.

Запрос

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


Стоит отметить, что идентификатор важен, если вы хотите отменить уведомление. Мы уже говорили, что вы создаете уведомление в приложении. Но ОС берет верх и дополняет список. Если у вас нет идентификатора, вы не можете попросить ОС отменить его.

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: "my_notification_id")

Ниже приведен пример кода типов уведомлений:

// 1. Create Local Notification and Add Content
let content = UNMutableNotificationContent()
content.title = "Local Notfication Title 🙂"
content.subtitle = "Local Notfication Subtitle"
content.body = "Local Notfication Body"
content.sound = UNNotificationSound.default
content.badge = 1 // You can handle this from BackEnd side, App can have mechanism to
// 2. Create Trigger and Configure the desired behaviour
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, // In 3s will send the Notification
                                                repeats: false) // Will send just Ones
// Choose a random identifier, this is important if you want to be able to cancle the Notification
let notificationIdentifier = UUID().uuidString + "some_uniq_identifier"
// 3. Create the Request
let request = UNNotificationRequest(identifier: notificationIdentifier,
                                    content: content,
                                    trigger: trigger)

// 4. Add our Notification Request to the que
UNUserNotificationCenter.current().add(request)
// 1. Create Local Notification and Add Content
let content = UNMutableNotificationContent()
content.title = "'Title': Local Notification 🙂"
content.subtitle = "'Subtitle': Calendar"
content.body = "'Body': Used 'DateComponents' to trigger it exactly at Time"
content.sound = UNNotificationSound.default
content.badge = 1
content.categoryIdentifier = "myActionCategoryIdentifier"
content.userInfo = ["customDataKey": "cusom_data_value"]

// 2. Create Trigger and Configure the desired behaviour - Calendar
var date = DateComponents()
date.year = 2022
date.month = 1
date.day = 30
date.hour = 15
date.minute = 08
date.second = 0
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: false)

// Choose a random identifier, this is important if you want to be able to cancle the Notification
notificationIdentifier = UUID().uuidString + "_" + "some_uniq_identifier" // E.g. 123-abc_clock-alarm
let request = UNNotificationRequest(identifier: notificationIdentifier,
                                    content: content,
                                    trigger: trigger)

// 3. Create the Request
UNUserNotificationCenter.current().add(request)

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


Вот код:

// 1. Create Local Notification and Add Content
let content = UNMutableNotificationContent()
content.title = "'Title': Local Notification 🙂"
content.subtitle = "'Subtitle': Location"
content.body = "'Body': Used 'region' to trigger when enter or exit"
content.sound = UNNotificationSound.default
content.badge = 1
content.categoryIdentifier = "myActionMessage"
content.userInfo = ["customDataKey": "cusom_data_value"]

// 2. Create Trigger and Configure the desired behaviour - Location
let location = CLLocationCoordinate2D(latitude: 45.255981,
                                      longitude: 19.844488)
let region = CLCircularRegion(center: location,
                              radius: 2,
                              identifier: UUID().uuidString)
let trigger = UNLocationNotificationTrigger(region: region, repeats: false) // Location trigger
// Choose a random identifier, this is important if you want to be able to cancle the Notification
notificationIdentifier = UUID().uuidString + "_" + "some_uniq_identifier" // E.g. 123-abc_clock-alarm
let request = UNNotificationRequest(identifier: notificationIdentifier,
                                    content: content,
                                    trigger: trigger)

// 3. Create the Request
UNUserNotificationCenter.current().add(request)

В разделе Content вы можете увидеть threadIdentifier. Вы можете использовать это для группировки уведомлений. Например, по уникальному пользователю или акции по сделке. До iOS 15 существовало свойство summaryArgument, которое было дополнением к группировке, но теперь оно устарело.

Слушайте события

На данный момент мы импортировали UserNotifications, зарегистрированные с помощью requestAuthorization(options:), но мы все еще не можем прослушивать события.

Следующим шагом является соответствие протоколу UNUserNotificationCenterDelegate. Вы можете сделать это несколькими способами. Я сделал это для простоты в AppDelegate, но вы можете создать отдельный Service/Manager или сделать это во ViewController, все это ситуативно и окончательного пути нет. 

Если вы хотите получать уведомления на переднем плане, то в методе userNotificationCenter(_:willPresent:withCompletionHandler:) это можно настроить, смотрите на completeHandler([.alert, .badge, .sound]).


Теперь вы можете прослушивать локальные/push-уведомления с помощью userNotificationCenter(_:didReceive:withCompletionHandler:) и данных userInfo. Если у него есть настраиваемые кнопки действий, я объясню их в следующем разделе.


Если вы хотите создать несколько локальных уведомлений, позаботьтесь о том, чтобы у них не было одного и того же идентификатора, так как новое сообщение переопределит старое с тем же идентификатором. Вот код:

import UserNotifications // 1. Import
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        UNUserNotificationCenter.current().delegate = self // 3. Add delegate what enables to fire some events
        registerForPushNotifications() // 2. Register 
        
        return true
    }
}

// MARK: - Notification 
extension AppDelegate: UNUserNotificationCenterDelegate { // 4. Make sure to conform to protocol
  // MARK: - Receive
    
    // Called when a notification is delivered to a foreground app.
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        
        completionHandler([.alert, .badge, .sound])
    }
  
    // Called to let your app know which action was selected by the user for a given notification.
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        // 1. Get the Notification Identifier
        let identifier = response.notification.request.identifier
        print("🆔 \(#function) Id: \(identifier)")
      
        // 2. Check for Custom Actions to Handle
        switch response.actionIdentifier {
        case UNNotificationDefaultActionIdentifier: print("Default Action identifier") // the user swiped to unlock          
        default: print("Switch default")
        }
      
        // 3. Check for Custom data passed with Notification
        let userInfo = response.notification.request.content.userInfo
        if let customData = userInfo["customDataKey"] as? String {
            print("Custom data received: \(customData)")
        }
        
        // You must call the completion handler when you're done
        completionHandler()
    }
}

Действие

Мы можем определить действия/кнопки для уведомлений. Для этого нам нужно определить действия с помощью UNNotificationAction. А затем добавить их в категорию с помощью UNNotificationCategory. Код показан ниже:

let center = UNUserNotificationCenter.current()

// 1. Create Custom Actions
let showMeMoreAction = UNNotificationAction(identifier: "showMeMoreIdentifier",
                                title: "Show me more",
                                options: [.foreground])
let snoozeAction = UNNotificationAction(identifier: "snoozeIdentifier", 
                                        title: "Snooze", 
                                        options: [])
let deleteAction = UNNotificationAction(identifier: "deleteIdentifier", 
                                        title: "Delete", 
                                        options: [.destructive])

// 2. Register Custom Actions For Category
let category1 = UNNotificationCategory(identifier: "myActionCategoryIdentifier1", 
                                       actions: [showMeMoreAction], 
                                       intentIdentifiers: [])
let category2 = UNNotificationCategory(identifier: "myActionCategoryIdentifier2", 
                                       actions: [snoozeAction, deleteAction], 
                                       intentIdentifiers: [])

// 3. Register Categories with OS
center.setNotificationCategories([category1, category2])

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

content.categoryIdentifier = "myActionCategoryIdentifier1"

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

Когда вы получите следующее уведомление и потяните уведомление вниз или нажмите и удерживайте его, вы увидите кнопки действий. Внешний вид кнопок может различаться в разных версиях iOS.

Чтобы определить, какая кнопка нажата пользователем, измените метод userNotificationCenter(_:didReceive:withCompletionHandler:) со следующими строками кода:

// 2. Check for Custom Actions to Handle
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier: print("Default Action identifier") // The user swiped to unlock
case UNNotificationDismissActionIdentifier: print("Dismiss Action identifier") // The user dismissed the Notification
case "showMeMoreIdentifier": print("👆 showMeMoreIdentifier")
case "snoozeIdentifier": print("👆 snoozeIdentifier")
case "deleteIdentifier": print("👆 deleteIdentifier")

default: print("Switch default")
}

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

APN (служба push-уведомлений Apple)

На данный момент мы почти готовы к push-уведомлениям. Но прежде чем мы перейдем к этому, нам нужно понять, как зарегистрироваться для получения push-уведомлений. APNs — это служба Apple, которая позволяет нам запрашивать Push-токен.

Состояние разрешения уведомлений

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

  • notDetermined — у пользователя не запрашиваются разрешения
  • Denied — Пользователю выдается предупреждение, но ему отказано.
  • authorized — пользователю предоставляется предупреждение, и пользователю разрешено
  • provisional — пользователь не запрашивается напрямую, но в коде устанавливается для этого типа разрешения
  • ephemeral — приложению разрешено планировать или получать уведомления в течение ограниченного периода времени. Например, AppClip может сделать это с некоторой настройкой в Info.plist. Подробности по следующей ссылке.
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { (settings) in
    switch settings.authorizationStatus {
        case .notDetermined: print("notDetermined")
        case .denied: print("denied")
        case .authorized: print("authorized")
        case .provisional: print("provisional")
        case .ephemeral: print("ephemeral")
        default: break
    }
})

Всплывающие уведомления

Весь пользовательский интерфейс, который мы видели до сих пор с локальными уведомлениями, можно применять и с push-уведомлениями. Единственное отличие состоит в том, что push-уведомления запускаются удаленно и должны проходить через APN.


Учитывая конфигурацию, сначала нам нужно включить push-уведомления для проекта в Xcode.

Часть регистрации почти такая же, как и с локальным уведомлением. На самом деле, мы будем использовать тот же метод, что и выше, requestAuthorization(options:). Разница в том, что теперь мы также хотим вызвать

UIApplication.shared.registerForRemoteNotifications()
// 1. Create Options
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
// 2. Call Request Authorazion with Options
UNUserNotificationCenter.current().requestAuthorization(options: [options]) { granted, _ in
    guard granted else { return }

    DispatchQueue.main.async {
        // 3. Register For Remote Notifications
        UIApplication.shared.registerForRemoteNotifications() 
    }
}

Это вызовет приложение (_:didRegisterForRemoteNotificationsWithDeviceToken:), и токен будет получен. Или в случае сбоя приложения (_:didFailToRegisterForRemoteNotificationsWithError:)

Токен выглядит примерно так: 9340516ea8a5ae6b149fa90c07efc1f738b2b0d38463cd326858921fc0d93a91 

Поскольку APN являются центральной точкой, откуда мы получаем токен удаленного уведомления, иногда брандмауэры не хотят пропускать. Особенно в случае с беспроводной связью. По крайней мере, это мой опыт и предположение. Может помочь сброс Wi-Fi, роутера или переключение на сотовую связь.Кроме того, имейте в виду, что это асинхронный процесс. Получение токена может занять некоторое время — секунды, а иногда и минуты.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { data in String(format: "%02.2hhx", data) }.joined()
    print("🆔 \(#function) Token: \(token)")
}

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

Существует множество таких сторонних сервисов, таких как LeanPlumCleverTapOneSignalFirebaseCloud MessagingUrbanAirship и т. д. Это может значительно упростить интеграцию.

Тестирование push-уведомлений

Если вы используете симулятор, не волнуйтесь. Есть способ сделать это.

Во-первых, вам нужен файл с расширением .apns. Что-то вроде payload.apns. Он имеет структуру, подобную JSON.

{
    "Simulator Target Bundle": "com.example.yourAppBudle",
    "aps" : {
      "alert" : {
        "title" : "Simple title",
        "body" : "Simple body"
      }
    }
}

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

Наиболее часто используемые свойства файла apns: 

  • alert— заголовок, подзаголовок, тело, которое говорит само за себя
  • badge— количество красных точек.
  • sound — строка «по умолчанию» для воспроизведения звука по умолчанию или «my_sound.mp3» из пакета.
  • category — это строка, которая будет определять идентификатор категории кнопок действий из локальной части уведомлений.
  • thread-id — это идентификатор, который используется для группировки сообщений.
  • Content-Available — это флаг фонового уведомления. «Чтобы выполнить фоновое обновление без вывода сообщений, укажите значение 1 и не включайте оповещения, значки или звуковые клавиши в свою полезную нагрузку». — Яблоко
  • mutable-content — если это значение равно 1, это означает, что используется расширение приложения-службы. Позже я расскажу об этом подробнее.
  • interruption level — это появилось с появлением iOS 15. Строковые значения «passive», «active», «time-sensitive» или «critical».

 Подробный список можно найти по этой ссылке.

{
   "aps" : {
      "badge" : 9,
      "sound" : "my_sound.mp3",
      "alert" : {
         "title" : "Simple Push Notification Title",
         "subtitle" : "Simple Push Notification Subtitle",
         "body" : "Simple Push Notification Body"
      },
      "category" : "myActionCategoryIdentifier1"
   },
   "customKey" : "12345678"
}

Еще один способ — протестировать push-уведомления из Терминала.

$ xcrun simctl push device_identifier com.example.yourApp payload.apns

Вам нужно заменить device_identifier. Вы можете увидеть, как найти это значение, на следующем изображении.

Если в полезной нагрузке у вас есть «Simulator Target Bundle», то вводить команду не нужно. Если вы правильно настроили команду, то на терминале появится что-то вроде «Уведомление отправлено на «com.example.myApp».

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

Тихие push-уведомления — это сообщения без предупреждений, пользовательского интерфейса и звука, которые можно получать в фоновом режиме. Чтобы иметь возможность использовать тихие push-уведомления, недостаточно настроить полезную нагрузку с «mutable-content»: 1 . Нам также необходимо настроить приложение. Нам нужно включить фоновые режимы для удаленных уведомлений.

Тихие push-уведомления не могут содержать alert, title, body или звук и т. д.

{
   "aps" : {
      "content-available" : 1
   },
   "customKey1" : "value1",
   "customKey2" : 42
}

Тихие push-уведомления с практической точки зрения используются для инициирования некоторых вызовов API фоновой выборки, для запуска некоторых обновлений, для возможности выполнения задачи в фоновом режиме и т. д. Это пробуждает приложение из фона.

Количество значков

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

Если у вас нет кастомного механизма, то проще всего его сбросить. Чтобы сделать это в событии AppDelegate applicationDidBecomeActive(_:) (или sceneDidBecomeActive(_:) в случае SceneDelegate), вызовите следующую строку кода:

UIApplication.shared.applicationIconBadgeNumber = 0

Rich Media (мультимедийное уведомление)

Это самая веселая часть. Мы можем использовать изображение, видео, gif или создать собственный UIView.


Push-уведомления имеют максимальный размер полезной нагрузки 4 КБ. С расширениями у нас есть возможность перехватить любое входящее push-уведомление и изменить заголовок, расшифровав любые зашифрованные данные или даже загрузив мультимедийные вложения.

Если вы не знакомы с расширениями, они похожи на мини-приложения, которые каким-то образом связаны с вашим основным приложением, объединенным внутри. Они являются «привратниками» вашего приложения. В случае с push-уведомлением у них есть возможность выполнить какое-либо действие до того, как ваше приложение получит push-уведомление.

{
    "aps": {
        "alert": {
            "title": "Rich Media Push Notification Title",
            "body": "Rich Media Push Notification Body"
        },
        "mutable-content": 1
    },
    "media-url": "https://some_image.jpg"
}

Как вы видите, «mutable-content»: 1 означает, что это мультимедийное push-уведомление.

Расширение службы уведомлений

В начале статьи вы могли видеть push-уведомление Rich Media с расширением видео (mp4). Ниже вы можете увидеть один с фото:

В нашем случае у Service Extension есть шанс загрузить какое-то изображение, видео или gif, в зависимости от того, какой URL находится в полезной нагрузке. Например, ОС дает сервисному расширению некоторое время, чтобы изменить заголовок, тело или, возможно, загрузить изображение. Если вы не можете изменить контент, он вернется к обычным push-уведомлениям с исходным контентом из полезной нагрузки.


Чтобы добавить в проект расширение службы уведомлений, перейдите в меню -> Файл -> Создать -> Цель. Выберите Расширение службы уведомлений и нажмите кнопку Далее.

Посмотрите на идентификатор организации и bundle идентификатор. Идентификатор расширения основан на идентификаторе вашего основного целевого пакета приложения, разница заключается в постфиксе. Таким образом, iOS знает, в какое приложение встроено расширение.

С помощью метода didReceive(_ request:) мы можем перехватывать и изменять содержимое.serviceExtensionTimeWillExpire() — это запасной вариант на случай, если мы не сможем изменить содержимое. Например, если загрузка образа не удалась или заняла слишком много времени.

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        if let bestAttemptContent = bestAttemptContent {
            // 1. Try to Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
            
            contentHandler(bestAttemptContent)
        }
    }
    
    override func serviceExtensionTimeWillExpire() {
        // 2. Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

Полный код можно найти по следующей ссылке.

{ 
    "aps": {
        "alert": {
            "title": "Teamwork Makes Dream Work ",
            "body": "Rich Media Video"
         },
         "mutable-content": 1,
         "category": "myMediaServiceCategory"
     },
     "media-url": "https://sample_video.mp4"
}

Вы можете видеть, что Rich Media также может иметь кнопки действий. Вам необходимо зарегистрировать их в Service Extension. Но обработка крана должна выполняться в приложении. В нашем случае в AppDelegate.

let likeAction = UNNotificationAction(identifier: "likeIdentifier", 
                                      title: "Like ❤️", 
                                      options: [])
let saveAction = UNNotificationAction(identifier: "shareIdentifier", 
                                      title: "Share ⬆️", 
                                      options: [])
let category = UNNotificationCategory(identifier: "myMediaCategory",
                                      actions: [likeAction, saveAction],
                                      intentIdentifiers: [],
                                      options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])

Расширение содержания уведомления

Content Extension — это создание пользовательского представления. Расширение содержимого уведомлений использует NotificationViewController, который является подклассом UIViewController. Так же создает в папке раскадровку и Info.plist.Настройка аналогична сервисному расширению. Просто выполните предыдущие шаги.

Ниже вы можете увидеть действительно хороший пользовательский вид — карусель с несколькими изображениями, заголовками, субтитрами и кнопками. Каждое изображение может иметь свой текст. У каждого изображения может быть своя Deep-ссылка.

Чтобы иметь возможность использовать Content Extension в Info.plist, найдите UNNotificationExtensionCategory и его значение, которое необходимо добавить в полезные данные push-уведомления под ключом «category». 

Этот тип уведомления может даже иметь интегрированные карты или любую структуру.Конечно, держите его легким.

Теперь возможности практически безграничны.

Сертификаты

Я попытаюсь объяснить это просто и сосредоточусь на Push-сертификатах.Перейдите на https://developer.apple.com/account/ Сертификаты, идентификаторы и профили.

  • Нажмите на знак «+», сервис push-уведомлений Apple SSL.
  • Выберите идентификатор приложения (предположим, вы его создали)
  • Загрузите файл aps_xyz.cer.
  • Дважды щелкните его, перейдите в Keychain Access.app (cmd + пробел открывает Spotlight и введите Keyc…)
  • Вы должны увидеть Apple Sandbox Push Services: com.example.demo (ваш идентификатор пакета)
  • Щелкните правой кнопкой мыши
  • Экспорт «Яблочная песочница…»
  • Сохраните файл .p12.

Серверная часть в основном будет запрашивать этот файл .p12, чтобы иметь возможность отправлять push-уведомления через APN. Но лучше создать файл ключа /.p8.

Совет

Срок действия push-сертификатов истекает через год, и вы должны обновлять их в центре разработчиков Apple и каждый год повторно загружать новый сертификат в ваш push-провайдер/серверную часть. Если у вас больше приложений, этот процесс необходимо выполнять каждый год для каждого приложения. Файл p8/key работает со всеми вашими приложениями как в среде разработки, так и в рабочей среде, и срок его действия не истекает.

Инструменты

Вот инструменты, которые можно легко протестировать на устройстве:


https://github.com/onmyway133/PushNotifications 👈 Моя рекомендация

NWPusher

Заключение

Теперь вы знаете, что делать с уведомлениями пользователей, как их называет Apple. Как я уже сказал, мне понадобились годы и годы, чтобы собрать всю информацию и изучить ее. Пожалуйста, не стесняйтесь рассказать мне что-то, что может меня заинтересовать, но я не знаю.

Если вы дошли до этого места, спасибо за чтение. Вы заслуживаете кофе ☕️. 🙂 Если вам понравился контент пожалуйста 👏, поделитесь, подпишитесь, это значит для меня. Если у вас есть предложения или вопросы, пожалуйста, не стесняйтесь комментировать.