1.什么是远程推送通知?
2.为什么需要远程推送通知?
3.所有的苹果设备,在联网状态下,都会与苹果的服务器建立长连接
什么是长连接? 只要联网了,就一直建立连接
长连接的作用: 时间校准 系统升级 查找我的iPhone
长连接的好处: 数据传输速度快 数据保持最新状态
4.远程推送原理 客户端发送设备的UDID和程序的bundle ID请求苹果服务器(SSL安全),客户端获得Token号存储起来,客户端再将Token号和用户信息等(如QQ号等)绑定发送给公司服务器,公司服务器保存token号和账户的关联信息,在适当时候,公司根据token号再通知苹果服务器进行消息推送
远程推送原理
选择推送证书 2.打开Bundle ID设置,确保push选项是enabled状态,不是可点击edit进行编辑
保证是enabled状态
3.或点击编辑
点击编辑
打勾后进行配置
打勾后进行配置
4.配置成功后钥匙串中多了一个证书,一个调试,一个push
钥匙串
要注意,由于iOS8 以后推送需要用户授权,所以AppDelegate中要分别适配不同版本
注册推送
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
打印Token号
注意:安装程序之后,无论运行多少次,Token都不应该发送改变! 但是在Xcode7中这个选项打开和关闭的Token值不一样!打开的时候才是真正的Token值!
打开推送选项
代码优化: 上面方法每次都需要请求Token,Token号只有第一次才需要请求,所以可以进行判断第一次才需要请求Token 可以用一个字典包装Token号,并存起来,下次读取如果字典里有值就不需要再请求了 注意:如果客户端更换了用户信息,就需要重新请求Token,删除本地信息重新请求,并删除公司服务器端Token信息(也可不删除添加一个),保证推送到新登录的账户上
3.模拟服务器测试推送:
Easy APNs Provider 工具 PushMeBaby 工具
PushMeBaby 使用方法 (1)导入推送调试证书文件
导入推送证书
(2)更改 ApplicationDelegate 中init方法中的对应值
修改Token号
(3)运行,点击推送
点击推送
(4)推送成功程序右上角就会有一个1的角标
推送成功!
4.接收到通知后程序回调的代理方法
注意:要考虑三种情况,后台、前台、退出程序。远程推送和本地推送一样,都需要在两个地方做代码的处理:
didFinishLaunchingWithOptions
方法中(退出状态),用 launchOptions [UIApplicationLaunchOptionsRemoteNotificationKey]
获取远程通知对象(1)前台和后台的推送回调这个代理方法(退出的设置在didFinishLaunchingWithOptions方法中)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
(2)前台、后台和退出的推送都会调用这个代理方法(iOS 7之后可用)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
注意:
(1)如果实现了该方法会导致上面方法失效 (2)会有如下警告:在方法中调用下handler就好了,注意该handler需要一个参数 completionHandler(UIBackgroundFetchResultNewData);
处理警告
(3)还有警告,需要添加一个值在info.plist中,可用到界面把后台模式更改一下
处理警告
勾选remote notifications
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 设置要注册的通知类型,iOS8以后配置授权
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// 如果是iOS8以前,设置要注册的通知类型
UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:type];
}
// iOS7之前,如退出程序后接收到推送,想要处理获取通知后的事件要在下面代码中
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
// 获取到通知对象, 进行处理
NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey][@"userInfo"];
// 退出程序测试方法,真正接收到了通知就在界面上创建一个红色的View(控制台无法打印)
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 375, 200)];
label.text = userInfo.description;
label.numberOfLines = 0;
[self.window.rootViewController.view addSubview:label];
}
return YES;
}
#pragma mark 远程推送注册完毕, 服务器返回Token时, 调用此方法
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// 将来要在这里将Token 发送给自己的服务器做保存
NSLog(@"deviceToken: %@", deviceToken);
}
#pragma mark 接收到远程推送的消息时调用此方法(后台和前台时可用)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// 记得要判断用户是否在前台
if (application.applicationState == UIApplicationStateActive) {
NSLog(@"不要自动跳转/ 给用户提示");
return ;
}
// 将来需要取消角标的数字, 是根据用户是否做了某些操作, 来更改数字角标的值
// 获取推送的值
NSInteger count = [userInfo[@"aps"][@"badge"] intValue];
// 设置相关的属性
application.applicationIconBadgeNumber = count;
}
#pragma mark 接收到远程推送的消息时调用此方法(前、后、退出都可用,iOS7以后可用)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// 测试添加一个label表示接收到通知
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 375, 200)];
label.text = userInfo.description;
label.numberOfLines = 0;
[self.window.rootViewController.view addSubview:label];
// 不调用该block会报错
completionHandler(UIBackgroundFetchResultNewData);
}