首先有关于证书的问题我们这里就不一一讨论了,证书有关的可以在百度上一找一大把的,就是需要注意将环境区分开就可以了。
这里可以将APP的运行状态分成两种状态:
1、APP处于前台的状态,顾名思义,就是APP在运行的状态下,处于一个长连接的一个状态下。
2、当APP被杀死之后,或者是处于非活跃的状态。非活跃的状态一般的可以理解为是APP在进入后台后一定的时间里所处于的状态,具体的可以参考我的另一篇文章iOS 后台机制。那么我们现在要讨论的就是关于APP处于非活跃(被杀死)的状态下的时候,收到远程推送的处理。当APP的长链接被断开的时候,Server 会通过APNs将消息以远程推送的形式下发到客户端。 此状态下如果有人给该用户发送消息,服务器会根据deviceToken和推送证书将消息发送到苹果推送服务器,苹果服务器会将该消息推送到客户端。
一、开发环境与生产环境
苹果推送服务器区分开发环境(Development)和生产环境(Production),两个环境的服务器不同,使用的 P12 证书不同,完全隔离。
有几点需要注意的。
1、deviceToken是唯一标识客户端的凭证,所以必须上传应用服务器才能使用远程推送。
2、模拟器收不到远程推送。
3、越狱的设备APNs服务不能保证,所以不一定能收到远程推送。
4、APNs 使用BundleID区分 App,使用通配符BundleID的 App 将无法使用远程推送。
二、现在当我们在不同的情况下接收到通知的时候:
在不同的系统中对远程推送的处理是不一样的。这里先讨论的是iOS 6和iOS 7以后的,iOS 10的改变很大,我们可以单独讨论:
1. 客户端在前台。通常是弹窗
● iOS6 的-(void) application: didReceiveRemoteNotification:被调用
● iOS7 的-(void) application: didReceiveRemoteNotification: fetchCompletionHandler:被调用
所以,为了兼容iOS6.通常的做法是,在-(void) application: didReceiveRemoteNotification: fetchCompletionHandler:中调用-(void) application: didReceiveRemoteNotification: 方法。
● push处理模块,判断一下程序是否是活跃 [UIApplication sharedApplication].applicationState == UIApplicationStateActive。
进而判断是否需要弹出对话框。
2. 客户端在后台,但是还活着
大致原理同第一条,注意处理是否弹框。
3. 客户端被杀死的状态,进来push消息;滑动(点击)push消息调起客户端。
● iOS 6调用 -(void) application: didFinishLaunchingWithOptions:带有launchDictionary,里面包含了push消息
需要简单处理一下,获取真正的内容[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]
● iOS 7以后调用两个方法:
-(void) application: didFinishLaunchingWithOptions:
-(void) application: didReceiveRemoteNotification: fetchCompletionHandler:
但是这样就有一个坑。在项目中也遇到了,特别是在推一个H5的页面的时候,你会发现可以进入到相关的页面,但是当返回或者是进入下一层级就会出现视图加载到window上去了;iOS7以上,这样做就重复处理同一个push消息了。
【解决】:由于只有iOS7以上都会调用-(void) application: didReceiveRemoteNotification: fetchCompletionHandler:方法。
那么,为了兼容iOS6:在-(void) application: didFinishLaunchingWithOptions:中只处理iOS6。
4.现在的APP都有一个启动图过程。如果push消息过来,APP正好是被杀死状态,且假设启动后有启动图。
这中push拉起APP后是直接处理push的,不弹框。
【解决】首先想到的方法是:暂存这个push消息,然后等启动图完了再展示push内容。注意,等到启动图完成,再使用push模块
[UIApplication sharedApplication].applicationState == UIApplicationStateActive就不好用了,因为此时APP应该是活跃的了,就需要弹框了。所以,需要push模块支持一个强制不弹框的接口。
三、iOS 10推送的适配
1.首先需要到项目工程的target中的Capabilities中打开后台的开关
2.iOS 10中新增了UserNotifications Framework
当APP在前台的时候调用的是下面的这个方法,而不是原来的那个方法了
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void(^)(UNNotificationPresentationOptions))completionHandler
当退出APP的时候调用的方法是:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
而且在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法中注册通知的方法也是不一样的:
if(IOS10) {
UNUserNotificationCenter *center =[UNUserNotificationCenter currentNotificationCenter];
center.delegate =self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError *_Nullable error) {if (!error) { NSLog(@"succeeded!"); } }];
回调方法中,进行数据的处理:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
NSDictionary*userInfo =response.notification.request.content.userInfo;
//消息处理
}
对于本地通知来说并没有什么太大的改变
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification