美文网首页OC开发
PushKit 的简单实验

PushKit 的简单实验

作者: 谁动了我的芝麻糖 | 来源:发表于2017-10-23 17:06 被阅读459次

    最近为了解决工作中的问题,看到了iOS 8开始,苹果的PushKit。简单实验了一番,虽然最后也没有用这个神器解决问题,总结一下。

    1,PushKit是不同于APNs的另外一种推送,用于VoiP的业务。据说是只提供给网络电话APP使用,如果不提供此功能的APP使用了这个功能,会增加审核被拒的风险。

    2,需要单独申请证书,不能使用Wildcard证书,申请方法如下(不贴图了,文字足够了):
    1)进入开发者中心,登录;
    2)左侧选"Certificates, IDs & Profiles", 进入证书申请页;
    3)确认要加入PushKit功能的APP ID存在,左侧"Identifiers"->"App IDs",右侧列表查看希望使用PushKit的APP ID是否在列表里,如果不在,点右上角 + 手动添加bundle Id(一般情况下Xcode会自动创建);
    4)左侧"Certificates"->"All",右侧选 + ,增加一个证书;
    5)单选"VoIP Services Certificate" -> "Continue";
    6)Select an App ID for your VoIP Service Certificate 列表,APP ID 列表里选择要添加PushKit功能APP的bundle ID,然后"Continue";
    7)一直"Continue",中间需要选择一次CertificateSigningRequest.certSigningRequest,完成后,下载证书voip_services.cer进行安装(此证书需要在server端使用)
    8)检查证书在KeyChain中是否安装成功
    如果PushKit服务器需要使用pem文件,那么使用openssl生成文件,与APNs推送的类似。

    3,需要在Xcode中,给相应的APP(target)勾选 Capabilities。
    1)注意,Push Notifications也要是ON,否则无法获取注册token;
    2)Background Modes , 勾选 Voice over IP,Background fetch 和 Remote notifications

    配置结束,下面是代码部分。
    我实验的内容是,收到PushKit推送后,发送本地推送。
    APP端代码:

    AppDelegate.h
    // 添加如下代码
    #import <PushKit/PushKit.h>
    // 添加delegate
    PKPushRegistryDelegate
    
    AppDelegate.m
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
           
    // 注册PushKit
    PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue: nil];
    pushRegistry.delegate = self;
    pushRegistry.desiredPushTypes = [NSSet setWithObject: PKPushTypeVoIP];
    
    // 注册push权限,用于显示本地推送
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
      return YES;
    }
    
    // PushKit delete
    - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
        
    //去掉server发回来token中的<>,空格不去掉,具体情况视PushKit server决定
    NSString * tokenString = [[[credentials.token description] stringByReplacingOccurrencesOfString: @"<" withString: @""] stringByReplacingOccurrencesOfString: @">" withString: @""];
    NSLog(@"PushKit toke: %@", tokenString);
    }
    
    - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
        
    // 从payload中获取推送信息
    NSDictionary *dic = payload.dictionaryPayload;
    NSDictionary *apsDic = dic[@"aps"];
    NSString *msgId = dic[@"mssage_id"];
    
    // 此处也可以根据message_id获取消息具体内容,然后在block中发送本地推送
    // ...
    
    // 生成本地推送
    UILocalNotification* localNotification = [[UILocalNotification alloc] init];
    localNotification.fireDate = [NSDate date];
    localNotification.alertBody = apsDic[@"alert"];  
    localNotification.soundName = @"default";
    localNotification.alertTitle = @"一条来自PushKit的推送";
    localNotification.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys: msgId, @"msgid", nil];
    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
    }
    
    -(void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type {
    NSLog(@"didInvalidatePushTokenForType");
    }
    
    

    APP端的处理很简单。

    下面是PushKit server,我使用的是Mac上直接能使用的 PushMeBaby,github地址:https://github.com/stefanhafeneger/PushMeBaby

    Payload格式,可以任意自定义:
    {
    "aps": {
    "alert": "Hello"
    },
    "message_id": "xxxxxxxxxx"
    }

    遇到的坑:
    1,github下载到的是debug版;
    2,PushMeBaby使用的token只需要去掉<和>,空格不能去掉,去掉后APP收不到推送;(这是我最初一直发送推送APP收不到的原因)
    3,ApplicationDelegate.m中self.certificate是证书的路径,需要把下载到的证书添加进工程,具体方法:
    File->Add files to "PushMeBaby",直接把voip_services.cer文件添加进工程,必要时进行拷贝;
    确保cer文件的文件名与self.certificate设置的一致:

    self.certificate = [[NSBundle mainBundle] pathForResource:@"voip_services" ofType:@"cer"];
    

    4,release版本与debug中的差别:
    debug:

    // Establish connection to server.
    PeerSpec peer;
    result = MakeServerConnection("gateway.sandbox.push.apple.com", 2195, &socket, &peer); NSLog(@"MakeServerConnection(): %d", result);
    
    // Set server domain name.
    result = SSLSetPeerDomainName(context, "gateway.sandbox.push.apple.com", 30); NSLog(@"SSLSetPeerDomainName(): %d", result);
    // 30是前面字符串的长度
    

    release

    // Establish connection to server.
    PeerSpec peer;
    result = MakeServerConnection("gateway.push.apple.com", 2195, &socket, &peer); NSLog(@"MakeServerConnection(): %d", result);
    
    // Set server domain name.
    result = SSLSetPeerDomainName(context, "gateway.push.apple.com", 22); NSLog(@"SSLSetPeerDomainName(): %d", result);
    // 22是前面字符串的长度
    

    实验结果:
    1)APP在前台,收到PushKit推送后,发送本地推送,APP不提示;(与其他remote notification一致);
    2)APP在后台,或者被closed的状态下,都能收到PushKit推送,并发出本地推送。我在另外一个稍微复杂点的测试中,在收到PushKit推送和发送本地推送之间,加入了根据message id在服务器获取消息具体内容的操作,也是正常的,可见在APP closed时,由PushKit推送调起的网络访问也是正常的。
    3)APNs silent push优先级最低,然后是APNs普通推送,优先级最高的是PushKit推送。
    4)APNs silent push和PushKit推送都能在APP处于后台的时候,唤醒APP,运行30s,不同的是silent push在APP closed(用户手动killed,手机重启或者APP被系统回收)时不能唤醒APP,现象就是APP没有反应,而PushKit在APP closed的时候,仍然能够唤醒APP,执行一段代码。

    猜测,在APP closed状态下,系统收到silent push后其实有能力执行APP的一段代码,只不过silent push优先级低,所以被忽略了~

    相关文章

      网友评论

      • 毛豆家的老疙瘩:你好,想问一下 PushKit 是否能和APNS 一样 可以自定义参数和消息
        谁动了我的芝麻糖:@毛豆家的老疙瘩 我当时实验的时候没有太注意,但是 - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type 这个方法有payload应该可以自定义参数的,然后转成本地通知提示出来就按照自己情况设定就好。
      • humouroutlaw:你好,iOS11 在后台3分钟之后就不能唤醒了,你有没有试过
        谁动了我的芝麻糖:很抱歉,我实验的时候没有关注时长。现在手头也没有环境了。抱歉,不能回答您的问题。

      本文标题:PushKit 的简单实验

      本文链接:https://www.haomeiwen.com/subject/sbeauxtx.html