苹果推送分为两种情况,一种时离线推送,一种是在线推送(包括后台在线推送);
然后有两种推送模式:1.走苹果APNS推送。2.走第三方推送
其中走苹果推送肯定可以应对两种推送情况;
然后走第三方推送的话,在线推送(包括后台在线推送)可以实现,但是离线推送就无法实现了(不可能让程序一直在后台运行,那样太费手机)
因此我们推送一般都是用的苹果APNS推送.
然后就是看看推送总体流程:
2、客户端接受deviceToken。
3、客户端将deviceToken发送给后台服务器程序。
4、后台服务器程序向APNS服务发送消息(根据deviceToken区别给哪个设备发消息)。
5、APNS服务将消息发送给iPhone应用程序(根据deviceToken发给相应设备)。
有CSR文件后开始注册APNS证书
上图那个Apple Push Services 即是生产环境下注册的APNS证书,下面那个就是开发环境下的APNS证书,个人感觉生产环境下的证书就能当开发环境下的证书使,不过为了区别还是分开比较规范。
证书注册完,下载下来双击就安装了。 如下图所示方框内即为两个APNS证书
然后需要在用地的证书右键->导出 ,生成p12文件给后台服务器,因为后台服务器给APNS发消息也是要证书认证的;(后台服务器是测试环境则配development的证书,正式环境则配生产证书)
didFinishLaunchingWithOptions方法中,进行判断用户设备是否已经注册,因为第一次启动需要注册同时需要存本地一个deviceToken的值通常存UserDefaults中即可,再次启动就检测下是否已经注册,已经注册就直接使用已经注册的deviceToken就行
其中 kEZApnsToken 为自己宏定义deviceToken的在UserDefaults中存储的Key
- (void)checkApns { //已经注册 if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) { if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) { //如果用户打开了推送 但是设置解锁后的提醒样式为"无"并且没有打开声音或者应用图标标记任何一个的时候 系统判断的是用户关闭推送(现在暂时没有更好的方法解决这一问题,所以暂时任务此种情况为用户关闭了推送) // 用户关闭了推送 }else{ //用户打开了推送 如果本地设备的token为空 则重新注册推送 // NSString *token =[[NSUserDefaults standardUserDefaults] objectForKey:kEZApnsToken]; // NSLog(@"%@",token); if (![[NSUserDefaults standardUserDefaults] objectForKey:kEZApnsToken]) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }else{ [self syncApns]; //注册过了将deviceToken发送给后台服务器 同时通知服务器用户推送是打开状态,可以进行推送 } } //未注册 }else{ UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } }
- (void)syncApns { NSString *apnsToken = [[NSUserDefaults standardUserDefaults] objectForKey:kEZApnsToken]; if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) { //如果用户打开了推送 但是设置解锁后的提醒样式为"无"并且没有打开声音或者应用图标标记任何一个的时候 系统判断的是用户关闭推送(现在暂时没有更好的方法解决这一问题,所以暂时任务此种情况为用户关闭了推送) // 用户关闭了推送 /*将deviceToken及用户关闭推送的状态发送给后台服务器,此时不能推送*/ }else{ /*将deviceToken及用户允许推送的状态发送给后台服务器,此时可以推送*/ } }
2、客户端接受deviceToken。
#pragma mark - APNS 获取token ////APNS - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSString *apnstoken = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""]stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""]; [self saveApnsToken:apnstoken]; //存储deviceToken,并检测用户是否允许推送,将状态同步给后台服务器 }
//获取token失败回掉的方法 - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"获取设备token失败"); }
- (void)saveApnsToken:(NSString *)data { [[NSUserDefaults standardUserDefaults] setObject:data forKey:kEZApnsToken]; [[NSUserDefaults standardUserDefaults] synchronize]; [self syncApns]; //检测用户是否允许推送,同时将用户推送允许状态及deviceToken同步到后台服务器,方便后台服务器推送 }
<?php $deviceToken= ‘自己的deviceToken'; //没有空格 $body = array("aps" => array("alert" => 'message',"badge" => 2,"sound"=>'default')); //推送方式,包含内容和声音$$ctx = stream_context_create(); //如果在Windows的服务器上,寻找pem路径会有问题,路径修改成这样的方法: //$pem = dirname(__FILE__) . '/' . 'apns-dev.pem'; //linux 的服务器直接写pem的路径即可 stream_context_set_option($ctx,"ssl","local_cert","apns-dev.pem"); $pass = "xxxxxx";stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);// //此处有两个服务器需要选择,如果是开发测试用,选择第二名sandbox的服务器并使用Dev的pem证书,如果是正式发布,使用Product的pem并选用正式的服务器 $fp = stream_socket_client("ssl://gateway.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx); $fp = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx); if (!$fp) {echo "Failed to connect $err $errstrn";return;} print "Connection OK "; $payload = json_encode($body);$msg = chr(0) . pack("n",32) . pack("H*", str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload; echo "sending message :" . $payload ." "; fwrite($fp, $msg); fclose($fp); ?>
#pragma mark - 用户切换到后台收到推送时点击通知栏调用的方法 用户处于前台工作时接受到推送调用的方法 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { // application.applicationState == UIApplicationStateActive;//程序处在前台 [UIApplication sharedApplication].applicationIconBadgeNumber = 0; /*从userinfo中获取自己服务器设定好的字段比如:一个通知的ID,然后从请求后台服务器获取相应数据进行相应操作(页面跳转、弹框等等)*/ }
userinfo中数据如下:
从userinfo中获取自己服务器设定好的字段比如:一个通知的ID,然后从请求后台服务器获取相应数据进行相应操作(页面跳转、弹框等等)
如果是离线推送,点击推送进入程序后会调用didFinishLaunchingWithOptions,此时推送信息会包含在didFinishLaunchingWithOptions的参数launchOptions中,格式如下:
我们需要根据key值取出推送信息
NSDictionary *pushInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; /*从userinfo中获取自己服务器设定好的字段比如:一个通知的ID,然后从请求后台服务器获取相应数据进行相应操作(页面跳转、弹框等等) 注意:由于在执行didFinishLaunchingWithOptions方法时,客户端UI界面等工作可能还没执行,直接进行UI操作可能会崩溃,所以此处进行UI操作最好加个延迟执行,或者在页面加载出来后初始页面中进行UI操作 */
注意:由于在执行didFinishLaunchingWithOptions方法时,客户端UI界面等工作可能还没执行,直接进行UI操作可能会崩溃,所以此处进行UI操作最好加个延迟执行,或者在页面加载出来后初始页面中进行UI操作,否则可能崩溃
补充:
当然在客户端代码写完后,客户端可以先将与后台服务器交互的代码注释掉,客户端利用一些第三方工具进行APNS测试。
这里使用的是SmartPush,
下载链接 - https://github.com/shaojiankui/SmartPush
下载后直接运行就行:
如图所示
选择证书时选择注册的证书即可;
然后deviceToken需要运行一下程序,然后第二步获取真机的deviceToken后将其打印填入SmartPush中即可;
然后PayLoad中可以填写json数据,除了"aps"字段外,我们可以添加自己后台服务器字段(按需求)
然后环境根据证书选择(一般都选测试,因为真机调试)。
最后点击连接服务器,然后推送即可;
然后就走步骤5、了;
其中 APNS所接受的json数据格式大概例如:
{ "aps":{ "alert":{ "title":"标题", // iOS7、8设置无效,iOS9一般设置为appName "subtitle":"子标题", // 一般使用title就能满足需求 "body":"内容" }, "badge":1, //角标数 "sound":"default", //声音 "userinfo":{ //通知内容信息 "playid":"123", "username":"tom", "sex":1 } } }