从上图我们可以看到:
1、应用程序注册消息推送。
2、iOS从APNS Server获取device token,应用程序接收device token。
3、应用程序将device token发送给PUSH服务端程序。
4、服务端程序向APNS服务发送消息。
5、APNS服务将消息发送给iPhone应用程序。
关于deviceToken:
deviceToken生成:
远程通知首先要向苹果APNs服务器注册并且生成一个唯一的deviceToken(每个设备的客户端都独一无二)。根据向APNs服务器发送的Token key(包含了设备的UUID和App的Bundle Identifier)。deviceToken将会以NSData对象发送到对应请求的App上。然后App把此deviceToken发送给我们自己的服务器,就是所谓的Provider。Provider收到deviceToken以后进行储存等相关处理,以后Provider根据这个deviceToken来进行消息推送。
deviceToken用处:
deviceToken是推送服务器(Provider)在向应用推送消息时,找到对应的设备以及该设备上对应的应用,从而把此推送消息推送给此应用。
deviceToken唯一性:
deviceToken根据设备唯一标识和客户端唯一标识生成。确保了deviceToken唯一。唯一性并不是说一台设备上的一个应用程序永远只有一个deviceToken,当用户升级系统的时候deviceToken是会变化的。
注册远程通知(获取deviceToken)
注册远程通知的方法
一般都是在App启动完成的时候去注册远程通知注册方法调用一般都在didFinishLaunchingWithOptions:方法中
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// 在iOS8之前注册远程通知的方法,如果项目要支持iOS8以前的版本,必须要写此方法
UIRemoteNotificationTypetypes=UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert;
[[UIApplicationsharedApplication] registerForRemoteNotificationTypes:types];
// iOS8之后注册远程通知的方法
UIUserNotificationTypetypes=UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings*mySettings =[UIUserNotificationSettingssettingsForTypes:types categories:nil];
[[UIApplicationsharedApplication] registerUserNotificationSettings:mySettings];
}
处理注册远程通知的回调方法
// 注册成功回调方法,其中deviceToken即为APNs返回的token
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[self sendProviderDeviceToken:deviceToken];// 将此deviceToken发送给Provider
}
// 注册失败回调方法,处理失败情况
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {}
处理接收到远程通知消息分两种情况:前台和后台
application: didFinishLaunchingWithOptions:此方法在程序在第一次启动时调用,根据方法内代码判断是否有推送消息。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// userInfo为收到远程通知的内容
NSDictionary*userInfo=launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
// 有推送的消息,处理推送的消息
}
return YES;
}
各种状态下APP收到消息以及处理:
首先不管在前台还是在后台,如果设置后台模式为Remote Notifications
具体设置方式(或者在info.plist中配置了UIBackgroundModes):如图
TARGETS-Capabilities-Background Modes-Remote Notifications
此时不论App处于Foreground状态还是处于Background状态,收到远程推送消息的时候都会立即调用此方法。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ }
这里引入一个概念:
**推送唤醒(remote notifications)
**iOS7以前,当你收到推送消息时,你需要先打开应用,等待应用从网络上获取推送的信息之后,才能将信息呈现出来。而iOS7改变了这一过程。当系统收到推送消息时,不是首先提醒用户,而是唤醒对应的应用,让应用在后台获取对应的信息。当信息处理完成后,再提醒用户。一个很小的改变,但是可以很大的提升用户体验。同样,iOS系统也会限制这种推送消息的频率,防止系统被频繁唤醒影响续航。
此时需要更改推送的payload,如果想要使用推送来唤醒应用运行代码的话,需要在payload中加入content-available,并设置为1。
aps {
content-available: 1
alert: {...}
}
1.程序在前台(Foreground)时收到推送:
如果设置remote notifications,那么先执行相对应的方法,在后台收到推送也是如此。
在前台收到通知时,会调用下面这个方法,可以在这个方法里面实现收到通知时刷新或跳转界面的功能;程序在前台收到推送时通知栏不会弹出推送信息
-(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo{}
2.程序在后台时收到推送:
如果设置remote notifications,那么先执行相对应的方法,在前台收到推送也是如此。
如果用户点击通知栏信息进入程序会调用情况1中的方法,所以在情况1的方法里面需要根据程序在前台还是后台来执行不同操application.applicationState
3.当程序关闭时收到推送:程序关闭时收到推送时,用户点击通知栏信息进入应用的时会调用
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// 在此方法中一定要调用completionHandler这个回调,告诉系统是否处理成功
UIBackgroundFetchResultNewData, // 成功接收到数据 UIBackgroundFetchResultNoData, // 没有接收到数据 UIBackgroundFetchResultFailed // 接受失败
if (userInfo) {
completionHandler(UIBackgroundFetchResultNewData);
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
可操作通知类型收到推送消息时回调方法
// 此两个回调方法对应可操作通知类型,具体使用方法参考以上方法很容易理解,不在详细叙述
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler {
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
}
未读消息数量角标设置
设置角标
[UIApplication sharedApplication].applicationIconBadgeNumber=badgeNum;
这个方法可以设置应用程序的角标的数值。但是当程序关闭时,收到推送后我们就不能改变角标数值了。所以建议让服务端推送过来的信息里加上'badge' = 88这个键值对来确定角标的显示数值。这样程序在后台还是关闭,只要显示服务端传给我们的角标值就好了。
不过当我们阅读完一条消息的时需要告诉服务器,并且将[UIApplication sharedApplication].applicationIconBadgeNumber减一。
当客户端杀死情况走本地推送(当客户端开启走—(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions)。
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
// 当被杀死状态收到本地通知时执行的跳转代码
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NSDictionary *infoDict = [mnResource parseJSONStringToNSDictionary:notification.userInfo[@"info"]];
[self.delegate diaplayAlertWhenReceivePushInfo:infoDict];
}
到现在为止做iOS 推送也差不多算入行了吧(都是用的个推)
昨天调试个推送也让我蛋疼,调试了一晚上,其他系统的没什么问题,就有个手机是iOS 10.0.2的系统,不知道为什么走不通apns。
遇到这些问题不要慌,先定为到问题。下面列举自己踩过的个推方面的坑:
1.打包的证书与个推后台的证书环境不一致,导致推送失败。
2.服务端忘记设置了个推安卓和ios的key,没有区分,所以会向客户端下发两条推送,那是我解析的时候是蒙蔽的,json解析出来怎么会有两种key完全不同的字典。这时候alert很大几率显示null且不能跳转,因为你解析不出来。
3.上面那种讲到的情况,自己分析应该是
这里在说明一点在使用个推的时候上传个推平台的.p12要和打包的证书环境一致哦(生产环境和调试环境)
参考:http://www.jianshu.com/p/4b947569a548