最近為了解決工作中的問題谣辞,看到了iOS 8開始迫摔,蘋果的PushKit。簡單實(shí)驗(yàn)了一番泥从,雖然最后也沒有用這個(gè)神器解決問題句占,總結(jié)一下。
1躯嫉,PushKit是不同于APNs的另外一種推送纱烘,用于VoiP的業(yè)務(wù)杨拐。據(jù)說是只提供給網(wǎng)絡(luò)電話APP使用,如果不提供此功能的APP使用了這個(gè)功能擂啥,會增加審核被拒的風(fēng)險(xiǎn)哄陶。
2,需要單獨(dú)申請證書哺壶,不能使用Wildcard證書屋吨,申請方法如下(不貼圖了,文字足夠了):
1)進(jìn)入開發(fā)者中心山宾,登錄至扰;
2)左側(cè)選"Certificates, IDs & Profiles", 進(jìn)入證書申請頁;
3)確認(rèn)要加入PushKit功能的APP ID存在资锰,左側(cè)"Identifiers"->"App IDs"敢课,右側(cè)列表查看希望使用PushKit的APP ID是否在列表里,如果不在绷杜,點(diǎn)右上角 + 手動(dòng)添加bundle Id(一般情況下Xcode會自動(dòng)創(chuàng)建)直秆;
4)左側(cè)"Certificates"->"All",右側(cè)選 + 接剩,增加一個(gè)證書切厘;
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進(jìn)行安裝(此證書需要在server端使用)
8)檢查證書在KeyChain中是否安裝成功
如果PushKit服務(wù)器需要使用pem文件俊扳,那么使用openssl生成文件途蒋,與APNs推送的類似。
3馋记,需要在Xcode中号坡,給相應(yīng)的APP(target)勾選 Capabilities。
1)注意梯醒,Push Notifications也要是ON宽堆,否則無法獲取注冊token;
2)Background Modes , 勾選 Voice over IP茸习,Background fetch 和 Remote notifications
配置結(jié)束畜隶,下面是代碼部分。
我實(shí)驗(yàn)的內(nèi)容是,收到PushKit推送后籽慢,發(fā)送本地推送浸遗。
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權(quán)限,用于顯示本地推送
[[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發(fā)回來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"];
// 此處也可以根據(jù)message_id獲取消息具體內(nèi)容,然后在block中發(fā)送本地推送
// ...
// 生成本地推送
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收不到推送击碗;(這是我最初一直發(fā)送推送APP收不到的原因)
3,ApplicationDelegate.m中self.certificate是證書的路徑们拙,需要把下載到的證書添加進(jìn)工程稍途,具體方法:
File->Add files to "PushMeBaby",直接把voip_services.cer文件添加進(jìn)工程砚婆,必要時(shí)進(jìn)行拷貝械拍;
確保cer文件的文件名與self.certificate設(shè)置的一致:
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是前面字符串的長度
實(shí)驗(yàn)結(jié)果:
1)APP在前臺装盯,收到PushKit推送后坷虑,發(fā)送本地推送,APP不提示埂奈;(與其他remote notification一致)迄损;
2)APP在后臺,或者被closed的狀態(tài)下账磺,都能收到PushKit推送芹敌,并發(fā)出本地推送。我在另外一個(gè)稍微復(fù)雜點(diǎn)的測試中垮抗,在收到PushKit推送和發(fā)送本地推送之間氏捞,加入了根據(jù)message id在服務(wù)器獲取消息具體內(nèi)容的操作,也是正常的借宵,可見在APP closed時(shí)幌衣,由PushKit推送調(diào)起的網(wǎng)絡(luò)訪問也是正常的。
3)APNs silent push優(yōu)先級最低,然后是APNs普通推送豁护,優(yōu)先級最高的是PushKit推送哼凯。
4)APNs silent push和PushKit推送都能在APP處于后臺的時(shí)候,喚醒APP楚里,運(yùn)行30s断部,不同的是silent push在APP closed(用戶手動(dòng)killed,手機(jī)重啟或者APP被系統(tǒng)回收)時(shí)不能喚醒APP班缎,現(xiàn)象就是APP沒有反應(yīng)蝴光,而PushKit在APP closed的時(shí)候,仍然能夠喚醒APP达址,執(zhí)行一段代碼蔑祟。
猜測,在APP closed狀態(tài)下沉唠,系統(tǒng)收到silent push后其實(shí)有能力執(zhí)行APP的一段代碼疆虚,只不過silent push優(yōu)先級低,所以被忽略了~