Как обрабатываются push-уведомления в iOS, когда приложение открыто и когда закрыто?

Ответ

Механизм обработки зависит от состояния приложения (foreground/background или terminated).

Когда приложение открыто (foreground):

  • Уведомление доставляется напрямую в код приложения.
  • Для UserNotifications framework (iOS 10+) вызывается метод делегата UNUserNotificationCenterDelegate:
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // Решение: показать уведомление в интерфейсе или обработать тихо
        completionHandler([.banner, .sound, .badge])
    }
  • Для устаревшего подхода может вызываться application(_:didReceiveRemoteNotification:fetchCompletionHandler:) в AppDelegate.

Когда приложение закрыто (terminated) или в фоне (background):

  1. Система отображает баннер, звук и значок (badge).
  2. При тапе пользователя на уведомление:
    • Если приложение было закрыто, оно запускается, и в application(_:didFinishLaunchingWithOptions:) доступны данные уведомления в launchOptions[.remoteNotification].
    • Если приложение было в фоне, оно переходит в foreground, и вызывается application(_:didReceiveRemoteNotification:fetchCompletionHandler:) или userNotificationCenter(_:didReceive:withCompletionHandler:).

Практическое отличие: В foreground вы должны явно решить, как отобразить уведомление (например, через completionHandler). В background/terminated системный баннер показывается автоматически, а ваше приложение получает данные для обработки после взаимодействия пользователя.

Ответ 18+ 🔞

О, слушай, смотри, как эта хуйня работает с пуш-уведомлениями на айфоне. Это же надо, какая разница, где твоё приложение болтается — на переднем плане или в жопе у системы.

Когда приложение у тебя прямо перед рожей (foreground): Вот тут всё просто, как три копейки. Уведомление не лезет в баннер сразу, а прилетает прямиком в код. Ты сам решаешь, что с ним делать — показать или тихо обработать. Если ты юзаешь эту современную хуйню, UserNotifications (iOS 10+), то дергается метод у делегата:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            willPresent notification: UNNotification,
                            withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // Вот тут ты и решаешь: показать интерфейс или молча в сраку закинуть
    completionHandler([.banner, .sound, .badge]) // Ну, показываем, допустим
}

А если ты старовер и сидишь на старом AppDelegate, то может вызваться application(_:didReceiveRemoteNotification:fetchCompletionHandler:). В общем, суть одна — ты в курсе и всё контролируешь.

А вот когда приложение закрыто (terminated) или в фоне (background): Тут уже начинается магия, блядь.

  1. Система сама, без спросу, вываливает тебе баннер, пищит и обновляет бейдж. Твоё приложение в этот момент может вообще спать, как сурок.
  2. Пользователь тыкает в это уведомление. Вот тут-то и просыпается вся ебля:
    • Если приложение было полностью убито, оно запускается заново. Данные от пуша лежат в launchOptions[.remoteNotification] внутри application(_:didFinishLaunchingWithOptions:). Найди их там, епта.
    • Если приложение просто висело в фоне, оно выпрыгивает на передний план, и вызывается либо старый метод в AppDelegate, либо новый userNotificationCenter(_:didReceive:withCompletionHandler:).

Короче, практическая разница, как небо и земля: Когда на переднем плане — ты царь и бог, сам решаешь, показывать уведомление или нет. А когда в фоне или убито — система сначала сама всё показывает, а твоё приложение получает пинок и данные только после того, как юзер на это дерьмо тапнул. Вот такая, блядь, хитрая жопа с уведомлениями.