最近推送一直有一些问题,app icon的badge显示问题,推送接收处理问题等,发现自己一直不太清楚整个流程,于是从头梳理了一遍,助于理解记忆。
一、推送接入
在 application(_:didFinishLaunchingWithOptions:)
中加入如下代码
if #available(iOS 10.0, *) {
// version >= iOS 10
let center = UNUserNotificationCenter.current()
center.delegate = self
var options: UNAuthorizationOptions = [UNAuthorizationOptions.alert, .sound, .badge]
if #available(iOS 12.0, *) {
// iOS12 新增加了 providesAppNotificationSettings,可视需求添加
options = [.alert, .sound, .badge, .providesAppNotificationSettings]
}
// 弹出 允许授权弹窗
center.requestAuthorization(options: options) { granted, error in
if granted {
// 用户允许进行通知
center.getNotificationSettings{ (settings) in
print("settings = \(settings)")
}
} else {
// 用户点击了不允许
}
}
} else {
// iOS 8 <= version < iOS 10
let userNotificationSettings : UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert,.sound,.badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(userNotificationSettings)
}
// 注册获取 device Token
UIApplication.shared.registerForRemoteNotifications()
在iOS12,UNAuthorizationOptions
新增了criticalAlert
、 providesAppNotificationSettings
、 provisional
,作用如下
// 重要警报,可无视勿扰模式和铃声开关的限制进行提醒,适用于灾难预警等重要信息
// 需要单独申请权限
@available(iOS 12.0, *)
public static var criticalAlert: UNAuthorizationOptions { get }
// 在通知管理里直接进入app内部设置页面时使用(跳转需自己在代理方法中处理)
// 代理方法为 userNotificationCenter(_:,openSettingsFor),后续会讲
@available(iOS 12.0, *)
public static var providesAppNotificationSettings: UNAuthorizationOptions { get }
// 临时授权,通知会以隐式推送的方式推送,并带有两个权限按钮让用户自己选择之后正常推送还是关闭
// 隐式推送:出现在通知中心并可在应用图标上出现标记,但不会显示在锁定屏幕上,不会显示横幅,也不会播放声音
@available(iOS 12.0, *)
public static var provisional: UNAuthorizationOptions { get }
二、推送处理
当一条推送到达手机的时候我们需要做一些处理,比如icon上的badge变动,点击推送时候的具体跳转等,这些处理的代理方法和其调用时机如下
// 当content-available为1时可调用(即使app已被杀死),当silent_push = 1时为静默推送
// 此方法因为可无视app是否存活调用,所以可用于推送撤销等行为,减少推送事故的发生(避免像36氪一样...),后续会讲
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// 需要做的操作
// xxxx
completionHandler(.newData)
}
// app在前台时调用
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// 需要做的操作
// xxxx
}
// 通知被点击后进入app时调用
// 此方法内部可处理各个推送的action,后续会讲
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
// 需要做的操作
// xxxx
}
@available(iOS 12.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter!, openSettingsFor notification: UNNotification?) {
if let trigger = notification?.request.trigger, trigger.isKind(of: UNPushNotificationTrigger.self) {
// 从通知界面直接进入应用(通知左滑->管理->关闭...->在"xxx"中配置...->进入app)
// 从图一入口进入调用
}else{
// 从通知设置界面进入应用(系统设置下面的通知页面下面的app通知页面进入app)
// 从图二入口进入调用
}
}
ps:图上入口都是只有在添加providesAppNotificationSettings之后才会出现
如果接入的是jpush的话,jpush的下面几个代理方法会覆盖系统的方法,将只执行jpush的方法,不执行系统的方法
当app在前台运行时收到通知,系统会自动调用userNotificationCenter(_:willPresent:withCompletionHandler:)
,不过该条通知依旧会在通知中心显示,当用户在通知中心从该通知进入app时,会再次调用userNotificationCenter(_:didReceive:withCompletionHandler:)
,需要注意不要对通知做重复处理
三、进阶处理
Background 运行
当content-available = 1
时,系统可无视app存活状态调用application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
方法,在其它代理方法之前(需要在Xcode 中修改应用的 Capabilities 开启Remote notifications)
静默推送
当silent_push = 1
时,content-available
必须为1
,此时该条推送不会出现在用户任何可见区域,用户完全感知不到,badge也不会变化
推送覆盖
apns-collapse-id
可用于推送覆盖,比如推送Aapns-collapse-id = 100
发送成功之后,再次发送Bapns-collapse-id = 100
,用户将会只在通知中心看到通知B(在用户未点击通知A的前提之下,参考微信的消息撤销功能)
推送撤销
效果:推送A发送成功之后,再次发送B去实现撤销功能,用户在通知中心将看不到A和B(在用户未点击通知A的前提之下)
实现:A为普通推送,B需要为静默推送,并传递需要撤销的通知id,然后在代理方法中处理
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// 撤销通知
if #available(iOS 10.0, *), let removeMessageId = userInfo["removeMessageId"] as? String {
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [removeMessageId])
}
completionHandler(.newData)
}