refer to: How to handle remote notification with background mode enabled

这些年iOS推送功能已进化非常强大,从只能简单提示一串文字,到如今包括自定义事件、本地化文字、后台模式,已是一套成熟的系统。 这篇文章描述了一条通知到达设备,发生的一切。

调用了哪一个代理方法

那是推送到达设备时应回答的问题。

四个的回调:

application(_:didReceiveRemoteNotification:)

application(_:didReceiveRemoteNotification:fetchCompletionHandler:)

userNotificationCenter(_:willPresent:withCompletionHandler:) (iOS 10.0, *)

NO callback

UIApplicationDelegate中还有其它回调方法,但除非有必要,不然这个讨论中忽略掉。详细的如下:

当打开通知的时候调用:

application:didReceiveRemoteNotification: (iOS 3.0)- 这是原始API。现在已被application(:didReceiveRemoteNotification:fetchCompletionHandler:)_(iOS 7.0)取代。 application(:handleActionWithIdentifier:forRemoteNotification:completionHandler:)_(iOS 8-10)支持自定义通知事件 userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 支持自定义通知事件

An advanced topic we shelf off for now.

配置payload 和 静默推送

推送通知 payload 组成如下:

  • alert - the alert string and actions
  • badge
  • sound
  • content-available

content-available键是一个新特性,而且就是这个键式静默推送成为可能。如果要静默推送,你太同时需要在“Capabilities”的UIBackgroundModes中添加remote-notifcation。

当ontent-available存在payload中时:

  • 如果应用Suspended,系统会使它进入Background运行模式
  • 如果应用被用户杀死,什么事都不会发生,并且应用无法运行读取应用状态

A potential is pitfall:

注意事项:

content-available=1开启静默推送,但是关闭静默推送并不是content-available=0。而是删掉payload中的键。

不同场景

content-available启用时

应用处于Foreground

没有弹出框

application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)被调用 userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 被调用

应用处于 Background

系统会弹出提示框

application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS >= 7)被调用

应用处于 Suspended

应用状态会变为Background

系统会弹出提示框

application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)被调用 userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 被调用

应用被用户杀死

系统会弹出提示框

不会有回掉

content-available未启用时 (键被移除)

  • 应用处于Foreground 没有提示框 application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)被调用 userNotificationCenter(:willPresent:withCompletionHandler:)_ (iOS >= 10)被调用
  • 应用处于 Background 或者 Suspended 系统会弹出提示框 不会有回调,但是用户点击推送打开应用时 application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)被调用 userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 被调用
  • 应用没有runing时 系统会弹出提示框 不会有回调,但是用户点击推送打开应用时 先调用application(:didFinishLaunchingWithOptions:)_ 然后application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)被调用 userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 被调用

总结

如果应用处于Foreground,系统不会弹出提示框。当调用application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10) userNotificationCenter(:willPresent:withCompletionHandler:)_ (iOS >= 10)后才呈现某些UI元素。

启用content-available会使应用处于Background模式 (除非应用是被用户杀死) 然后调用 application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10) userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10)

若未启用 content-available, 应用会一直处于Suspended, 并且 application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10) userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10) 不会被调用,除非用户点击推送。

最后, application(:didFinishLaunchingWithOptions:)_ 只会在应用Not Runing时调用。用户点击推送,会调用 application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10) userNotificationCenter(:didReceive:withCompletionHandler:)_ (iOS >= 10)。所以: 推送后台处理都在:application(:didReceiveRemoteNotification:fetchCompletionHandler:)_。 推送前台处理:application(:didReceiveRemoteNotification:fetchCompletionHandler:)_ (iOS < 10)需要判断应用状态

//所有后台处理
if application.applicationState == .background {

    //对应的UIBackgroundFetchResult
    completionHandler(UIBackgroundFetchResult.newData)
}

//iOS < 10 用户点击通知消息处理
if application.applicationState != .background && ProcessInfo.processInfo.operatingSystemVersion.majorVersion <= 9{

}

userNotificationCenter(:willPresent:withCompletionHandler:)_ (iOS >= 10)