APNS,即Apple Push Notification Service苹果推送通知服务,是苹果的服务器。
推送通知可以分为三个阶段:
第一阶段:应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。
第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
1. Push的三个步骤
如下图所示:
(1)Push服务应用程序把要发送的消息、目的iPhone的标识打包,发给APNS;
(2)APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone;
(3) iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
具体如下图5个步骤,无论是iPhone客户端跟APNS,还是Push服务器和APNS都需要通过证书进行连接的:
2. native端代码实现
推送消息时,不同时机点击,代码调用情况:
1. 当程序处于关闭状态收到推送消息时,点击图标会调用- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 这个方法,那么消息给通过launchOptions这个参数获取到。
2. 当程序处于前台工作时,这时候若收到消息推送,会调用- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary *)userInfo这个方法。
3. 当程序处于后台运行时,这时候若收到消息推送,如果点击消息或者点击消息图标时,也会调用- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary *)userInfo这个方法。
4. 当程序处于后台运行时,这时候若收到消息推送,如果点击桌面应用图标,则不会调用didFinishLaunchingWithOptions和didReceiveRemoteNotification方法,所以无法获取消息。
(1)注册通知(当然,程序关闭,推送消息时点击图标的逻辑也应该写在这里)
// 1.注册通知
#import <UserNotifications/UserNotifications.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
if ([[UIDevice currentDevice].systemVersion floatValue] >8.0){
//iOS8 - iOS10
[[UIApplication sharedApplication] registerForRemoteNotifications];
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:
UIUserNotificationTypeAlert |
UIUserNotificationTypeSound |
UIUserNotificationTypeBadge categories:nil]];
}else if ([[UIDevice currentDevice].systemVersion floatValue] < 8.0) {
//iOS8系统以下
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound];
}
return YES;
}
补充知识点:
iOS 程序启动时总会调用application:didFinishLaunchingWithOptions:,其中第二个参数launchOptions为NSDictionary类型的对象,里面存储有此程序启动的原因。
- 若用户直接启动,lauchOptions内无数据;
- 若由其他应用程序通过openURL:启动,则UIApplicationLaunchOptionsURLKey对应的对象为启动URL(NSURL),UIApplicationLaunchOptionsSourceApplicationKey对应启动的源应用程序的bundle ID (NSString);
- 若由本地通知启动,则UIApplicationLaunchOptionsLocalNotificationKey对应的是为启动应用程序的的本地通知对象(UILocalNotification);
- 若由远程通知启动,则UIApplicationLaunchOptionsRemoteNotificationKey对应的是启动应用程序的的远程通知信息userInfo(NSDictionary);
- 其他key还有UIApplicationLaunchOptionsAnnotationKey,UIApplicationLaunchOptionsLocationKey,
UIApplicationLaunchOptionsNewsstandDownloadsKey。
所以,可以根据上面的key将lauchOptions取出来:
//apple push启动
NSDictionary *applePushDic = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
//本地通知启动
UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
(2)获得Device Token
// 2.获得Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken{
NSLog(@"regisger success:%@", pToken);
// 注册成功,将deviceToken保存到应用服务器数据库中,因为在写向ios推送信息的服务器端程序时要用到这个
}
// 3.获得Device Token失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
}
3010错误之外,经常会遇到“Error: Error Domain=NSCocoaErrorDomain Code=3000 "no valid 'aps-environment' entitlement string found for application"
(2.1)确保证书开通了push的功能:
可以在开发者网站上的App ID下查看,其Development、Distribution是否都已经开通了Push的功能。Development Push是你用Xcode调试时用的Debug使用的,Distribution的Push是正式包使用的。
一个Bundle ID开通了Development、Distribution的push是要生成对应的两个证书的。此时,正式证书的类型是"Apple Push Services",Development对应的证书的类型是"APNs Development iOS"。你的pp文件后面也要用的是对应的证书文件。
(2.2)确保配置文件信息正确:
证书开通了push之后,你的配置文件要使用的是你的对应的证书,对应的app id等,都要对应起来。本地如果重新安装配置文件的话,确保老的删掉后,重新安装,重新在Target》build setting》Provision Profile下选择你刚安装的证书。能重新选择才能确定这里选择的是新的而不是旧的。PS:删除老的provision文件的方法,可以在Xcode》Preference》Accounts》对应的Apple Id,对应的Team》View Details》Provision Profile,然后点击右键就可以选择在finder中查看,可以删除对应的Provision Profile文件了。
(2.3)确保Xcode的push设置正确:
Target》Capabilities下面,找到你的Push Notification,确保你的push功能开着。并且"Add the Push Notification feature to your App ID"和"Add the Push Notifications entitlement to your entitlements file"这两个steps是ok的。
(3)处理推送消息
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
//4. 处理推送消息
}