# 第一步:设置 注册远程通知
[[UIApplication sharedApplication] registerForRemoteNotifications];
如果使用极光的话,[JPUSHService registerForRemoteNotificationConfig:entity delegate:self]
; 包装实现了上述api的功能
- 注意:
-
registerForRemoteNotifications
可以直接调用来注册远程推送,而不需要用户允许。也就是说只要调用该方法,就可以在AppDelegate的application:didRegisterForRemoteNotificationsWithDeviceToken:
中获取到设备的device token。 - 那么通常的弹窗询问权限有什么用呢?其实只是请求用户允许在推送通知到来时能够有alert, badge和sound,而并不是在请求注册推送本身的权限。
-
用户不允许应用的推送,静默推送依然会送达用户设备
,只是不会有alert, badge和sound。这也符合静默推送的正常使用场景。
-
# 第二步:处理 注册远程通知成功、失败回调
/**
Typically, the system calls this method only after you call your WKExtension object’s registerForRemoteNotifications method, but WatchKit may call it under other rare circumstances. For example, WatchKit calls the method when the user launches an app after setting up the watch using a different device’s backup. In this case, the app doesn’t know the new device’s token until the user launches it.
如果这个方法不调用,那么APNS通知是收不到的,当然`应用内消息`可以照常使用
*/
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken API_AVAILABLE(ios(3.0));
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error API_AVAILABLE(ios(3.0));
有时候,会出现上面两个方法都不调用,导致推送接收不到。极光打印出Not get deviceToken yet.
可以从以下几个问题排查:
- 推送证书
- 是真机,且应用推送权限开启
- 确定调用了
registerForRemoteNotification
(不管是极光的还是原生的) - 网络问题
可以参考极光的文档:https://docs.jiguang.cn/jpush/client/iOS/ios_faq/
我的解决方案:飞行模式开启-关闭
,然后就好使了(更新:飞行模式、关机重启已经解决不了这个网络问题了,只能设置-还原网络设置
了,好使了...)
极光在能正确获取到deviceToken但收不到推送的情况:
我们是使用alias进行推送的,极光的逻辑是:
- JPush SDK 注册完成之后,生成一个RegistrationID(与deviceToken关联,是会变化的,每次变化生成一个新的时,极光后台统计数据,会计入一个用户新增),然后设置别名的本质是将
alias与RegistrationID
关联起来。参考文档1、参考文档2
一个alias下可以绑定多个RegistrationID,如果一个别名被指定到了多个用户,当给指定这个别名发消息时,服务器端 API 会同时给这多个用户发送消息。
如果我们为每台设备设置了唯一的一个别名(比如是idfa),但是用极光的API却查出这个别名下对应了多个设备(即RegistrationID),那么可能是在RegistrationID的有效期内没有调用
deleteAlias:
解除绑定(比如用户卸载,导致没机会解除)。极光提供了查询、删除接口来操作alias与RegistrationID之间的关系极光于 2020/03/10 对「别名设置」的上限进行限制,最多允许绑定 10 个设备,如果超出了,设置别名会失败
如果RegistrationID变化之后,没有在该设备上再次调用
setAlias:
重新绑定alias与RegistrationID,就会导致通过alias找不到目前设备正确的RegistrationID,导致推送收不到综上所述,客户端与服务端之间使用RegistrationID标识,来推送,更为好用一点
# 第三步:处理 设备接收到通知的回调
## iOS 10之前
/**
* 方法1: 兼容iOS 3 — iOS 10
* 过期后,分别由方法4 5来代替处理`用户可见远程通知`,由方法3来代替处理`静默远程通知silent remote notifications`
* If a remote notification arrives while your app is active, WatchKit calls this method to deliver the notification payload. 即只有当APP处于活跃状态时,收到远程通知,才会触发这个方法。
* 活跃分为:前台、后台活跃,当处于前台时不显示通知alert,直接触发这个方法(iOS 10之后如果想在前台显示alert,需要实现方法4,iOS10之前,则需要自己发local notification来显示alert)。当处于后台活跃状态时,会显示,点击通知才会触发这个方法
* 注意:如果实现了方法3,这个方法就不会被回调
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
/**
* 方法2: 兼容iOS 4 — iOS 10
* 过期后, 分别由方法4 5来代替处理本地通知
* If a local notification arrives while your app is active, WatchKit calls this method to deliver the notification payload. 更详细的没测试
*/
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
/**
* 方法3: 兼容iOS 7及以后
* Tells the delegate that a background notification has arrived. 即APP处于挂起、kill状态都会触发这个方法。触发 与alert显示情况与方法1相同:处于前台时,不alert,直接调用;挂起或kill状态,点击通知会触发
* 实现此方法来处理传入的后台通知。当通知到达时,系统会启动您的应用程序或将其从挂起状态唤醒,您的应用程序会收到在后台运行的一小段时间。
* 可以使用后台执行时间处理通知并下载其相关内容。一旦完成对通知的处理,就必须调用fetchCompletionHandler完成处理程序。您的应用程序有30秒的挂钟时间来处理通知并调用处理程序,时间到了系统会终止您的应用程序。请尽可能快地调用处理程序,因为系统会跟踪应用程序后台通知的运行时间、功耗和数据成本。
* 后台通知是低优先级的,系统根据目标设备的功率考虑限制这些通知。APNs不保证设备会收到推送通知,而那些在处理后台通知时消耗大量能量或时间的应用程序可能不会收到后台时间来处理未来的通知。
* 应用程序因为远程通知而启动或恢复,也将调用此方法。注意,此方法与方法1冲突,如果实现了此方法,则不会调用方法1
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
## iOS 10及以后
/**
* 方法4: 兼容iOS 10及以后
* 只有当应用程序位于前台时,才会在收到通知时调用该方法。如果方法没有实现,或者没有及时调用处理程序,则前台不会显示通知。应用程序可以选择以声音、徽章、警报和/或在通知列表中显示通知。取决于通知中的信息是否对用户可见
* 实现了该方法后,方法1 3会收到影响,在前台时,不再是不显示+直接触发方法1 3,而是触发本方法,点击之后触发方法1 3
*/
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler;
/**
* 方法5: 兼容iOS 10及以后
* 当用户通过打开应用程序、取消通知或选择UNNotificationAction来响应通知时,会调用该方法。必须在应用程序从application:didFinishLaunchingWithOptions:返回之前设置delegate。
*/
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler;
/**
* 方法6: 兼容iOS 12及以后
* 极光的解释:当从应用外部通知界面或通知设置界面进入应用时,该方法将回调。
*/
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification;
# 测试接收到APNS时,API的调用
实现方法1345
前台
显示+调用willPresent 点击调用didReceiveNotificationResponse
后台/非active状态
显示+谁都不调 点击调用didReceiveNotificationResponse
实现方法135
前台
不显示+调用didReceiveRemoteNotification:fetchCompletionHandler
后台/非active状态
显示+谁都不调 点击调用didReceiveNotificationResponse
实现方法134
前台
显示+调用willPresent 点击调用didReceiveRemoteNotification:fetchCompletionHandler
后台/非active状态
显示+谁都不调 点击调用didReceiveRemoteNotification:fetchCompletionHandler
只实现方法1 3
前台
不显示+调用didReceiveRemoteNotification:fetchCompletionHandler
后台/非active状态
显示+谁都不调 点击调用didReceiveRemoteNotification:fetchCompletionHandler
# 总结
方法1 2是在iOS 10之前,用来接收
remote notification
、local notification
的方法3,是为了实现
Silent/Background Remote Notifications
而出的API,但在功能的设计上,直接囊括了方法1的功能(当APNS消息中content-available = false
时,功能与方法1相同),所以只要实现了方法3,方法1就不会再被调用。
方法3比方法1功能更全面的一点是:方法1的方法是说明中,只有在active状态下,收到远程推送才调用。而方法3,在挂起、kill状态下,点击推送唤醒APP时,也会调用方法4 5是在iOS 10之后新出的框架
<UserNotifications/UNUserNotificationCenter.h>
用来取代之前的通知处理方式,并增加了很多新的特性,又将方法1的职责争取了过来,两者结合使用,使得方法123不再被调用(注意:在实现静默推送时,仍需用到方法3)当收到静默通知时,会自动触发方法3(无论是前台、后台、挂起状态)。
-
<UserNotifications.framework>
库带来的特性:- iOS 10之前,默认App在前台运行时不会进行弹窗,使得需要在方法3中,判断是前台状态,然后发送
local notification
。iOS 10之后,只需要实现方法4,调用completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
即可 -
UNNotificationServiceExtension
,可以实现在通知展示之前拦截,自定义,实现通知上富文本、图片的展示。另外,附带着终于可以实现iOS的送达数据统计 -
getDeliveredNotificationsWithCompletionHandler
可以获取通知中心已展示的通知 - 注意:在使用时,必须在
didFinishLaunchingWithOptions
返回之前设置代理:[UNUserNotificationCenter currentNotificationCenter].delegate = self;
- iOS 10之前,默认App在前台运行时不会进行弹窗,使得需要在方法3中,判断是前台状态,然后发送
-
此外,一些版本的关于通知的特性:
- iOS 8
UIUserNotificationSettings
:修改了推送的注册接口,在原本的推送type的基础上,增加了一个categories
参数,这个参数的目的是用来注册一组和通知关联起来的button的事件。 - iOS 12,关于推送,又出了一些新特性:比如APP推送分组、消息左滑出现通知管理等
- 可以查看极光的汇总:https://docs.jiguang.cn/jpush/client/iOS/ios_new_fetures/
- iOS 8
# 静默推送(silent/background remote notification)
iOS 7在推送方面最大的变化就是允许,应用收到通知后在后台(background)状态下运行一段代码,可用于从服务器获取内容更新。功能使用场景:(多媒体)聊天,Email更新,基于通知的订阅内容同步等功能,提升了终端用户的体验。用户不允许应用的推送,静默推送依然会送达用户设备。
如果只携带content-available: 1
,不携带任何badge,sound 和消息内容等参数,alert字段可以有,但value必须为空
,则可以不打扰用户的情况下进行内容更新等操作即为“Silent/Background Remote Notifications”
,如果不携带此字段则是普通的Remote Notification
。可以查看苹果文档Pushing Background Updates to Your App
客户端设置:需要在Xcode 中修改应用的Capabilities
,在Background Modes里面勾选Remote notifications(推送唤醒)
限制与注意
“Silent Remote Notifications”是在 Apple 的限制下有一定的频率控制,但具体频率不详。所以并不是所有的 “Silent Remote Notifications” 都能按照预期到达客户端触发函数。
“Background”下提供给应用的运行时间窗是有限制的,如果需要下载较大的文件请参考 Apple 的 NSURLSession 的介绍。
根据方法3的说明:系统会跟踪应用程序后台通知的运行时间、功耗和数据成本。
background remote notification
是低优先级的,系统根据目标设备的功率考虑限制这些通知。APNs不保证设备会收到推送通知,而那些在处理后台通知时消耗大量能量或时间的应用程序可能不会收到后台时间来处理未来的通知。“Background Remote Notification” 的前提是要求客户端处于Background 或 Suspended 状态,如果用户通过 App Switcher 将应用从后台 Kill 掉应用将不会唤醒应用处理 background 代码。(
这是极光文档中的说明,但是测试了一下,在前台收到也会触发方法3
)静默推送:收到推送(没有文字没有声音),不用点开通知,不用打开APP,就能执行
方法3
,用户完全感觉不到注意
:使用极光的Web页面进行推送测试,在iOS 13以下,无论是前台、后台状态都会触发方法3。但在iOS 13的系统中,只会在前台收到静默推送才会触发方法3(记录于2020-03-30)。查阅了一些资料,可能是apns-push-type: background
的问题(由于没有业务场景使用,并没有深究)。参考文档:iOS13 静默推送填坑、Apple Developer Document—Pushing Background Updates to Your App