PushKit 的简单实验

最近为了解决工作中的问题,看到了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 
// 添加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 的简单实验)