iOS 推送总结

苹果推送分为两种情况,一种时离线推送,一种是在线推送(包括后台在线推送);

然后有两种推送模式:1.走苹果APNS推送。2.走第三方推送

其中走苹果推送肯定可以应对两种推送情况;

然后走第三方推送的话,在线推送(包括后台在线推送)可以实现,但是离线推送就无法实现了(不可能让程序一直在后台运行,那样太费手机)

因此我们推送一般都是用的苹果APNS推送.

然后就是看看推送总体流程:

1、首先是客户端注册APNS推送(客户端跟APNS Server要deviceToken(用来区别设备的标识))。
2、客户端接受deviceToken。
3、客户端将deviceToken发送给后台服务器程序。
4、后台服务器程序向APNS服务发送消息(根据deviceToken区别给哪个设备发消息)。
5、APNS服务将消息发送给iPhone应用程序(根据deviceToken发给相应设备)。
 
下面来看具体开发流程
1、首先是客户端注册APNS推送(客户端跟APNS Server要deviceToken(用来区别设备的标识))。
要注册APNS推送,需要到苹果证书申请网站(即打包证书注册网站)注册APNS证书,然后要重新生成齿轮文件(即Provisioning Profiles文件);
如果有CSR文件则注册时使用原先的,没有则重新生成个就行了(如下图,点击后填邮箱、姓名然后保存到本地磁盘就生成了CSR文件)

有CSR文件后开始注册APNS证书

 

上图那个Apple Push Services 即是生产环境下注册的APNS证书,下面那个就是开发环境下的APNS证书,个人感觉生产环境下的证书就能当开发环境下的证书使,不过为了区别还是分开比较规范。

证书注册完,下载下来双击就安装了。 如下图所示方框内即为两个APNS证书

 

然后需要在用地的证书右键->导出 ,生成p12文件给后台服务器,因为后台服务器给APNS发消息也是要证书认证的;(后台服务器是测试环境则配development的证书,正式环境则配生产证书)

到此APNS证书配置结束
证书弄好后开始代码进行客户端注册注册APNS推送
在每次启动

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。
3、客户端将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同步到后台服务器,方便后台服务器推送
}
 
4、后台服务器程序向APNS服务发送消息(根据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);
?>
5、APNS服务将消息发送给iPhone应用程序(根据deviceToken发给相应设备)。
如果是在线推送进入程序后会直接调用下面方法。通过下面方法进行推送操作处理。
 
#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
      }
   }
}

更加详细格式可查看苹果官网 - https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1

原文地址:https://www.cnblogs.com/Ganggang888/p/7268384.html