• 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

  • 相关阅读:
    LeetCode——Basic Calculator
    LeetCode——Sqrt(x)
    LeetCode——Binary Search Tree Iterator
    LeetCode——Search for a Range
    LeetCode——pow(x, n)
    LeetCode——Single Number II
    LeetCode——Summary Ranges
    LeetCode——Largest Number
    LeetCode——Kth Largest Element in an Array
    LeetCode——Implement Stack using Queues
  • 原文地址:https://www.cnblogs.com/Ganggang888/p/7268384.html
Copyright © 2020-2023  润新知