APNS(Apple Push Notification service)可以给ios设备推送通知,直观表述就是当某个app在后台运行时,能弹出提示消息。相比Android来,只有这一套方案所以少了很多调研成本。标题写NodeJS,只是因为我们的生产环境是NodeJS,我也用其他语言调过,测试的库是这三个:node-apns(NodeJS) / PyAPNs(Python) / apns(Ruby)。本文的重点不是针对代码(因为确实没什么代码量),而是描述如何在Apple开发者中心请求证书、生成PEM文件、读取PEM文件和证书对APNs的沙盒发送请求,设备接收到Notification后进行处理这一系列流程的入门文章。
Tips & Preparations
APNs整套系统的原理文档中写得很详细,此处不赘述。只引用下图作为说明。
NodeJS要实现的是Provider(以下称服务)部分。
- 1. 需要申请Apple开发者帐号,准备一台真实设备。模拟器没有Device Token,不能测试APNS;
- 2. 收到提醒时,如果程序在前台打开会看不到提醒;
- 3. 如果连续推送内容相同的提醒会失败。
Generate Certificate
在Apple开发者中心创建App ID,创建时勾选App Services - Push Notifications。App ID Suffix选择通用App ID(Wildcard App ID)还是固定App ID(Explicit App ID)都可以,如果要开发多个App可以选择通用App ID,本文只做范例所以创建固定App ID,Name为dmyz,ID为org.dmyz.app。创建成功后在Identifiers - App IDs可以查看,点击之前创建的App ID,在展开的菜单中点击[Settings]按钮进入配置界面。
在配置界面中找到Push Notifications,点击[Create Certificate…]按钮创建SSL证书。Development SSL Certificate 和 Production SSL Certificate对应开发环境和生产环境,选择Development SSL Certificate。
也可以点击左侧导航菜单的Certificates,来添加一个APNs证书。选择Apple Push Notification service SSL (Sandbox),之后的步骤相同。
第一步要先生成CSR(证书请求)文件,在Mac系统(本文是10.8)中打开实用工具 - 钥匙串 - 钥匙串访问 - 证书助理 - 从证书颁布机构请求证书,填入邮箱和名称(dmyz),选择[存储到磁盘],得到一个名为CertificateSigningRequest.certSigningRequest的证书请求文件,同时会在登陆 - 密钥里新增两条纪录,一个公用密钥和一个专用密钥:
回到Apple开发者中心的网页,点击[Continue]继续,上传生成的CertificateSigningRequest.certSigningRequest文件。完成这一步就可以得到.cert文件了。
Provisioning Profiles
在ios设备上运行程序需要Development Provisioning profile,这一步和APNs没有直接联系,已经在真实设备上配置成功的可以跳过。
App开发过程中要在真实设备上调试,需要Development Provisioning profile和Development Certificate。
在左侧导航菜单 - Devices,添加一个ios设备。UUID可以在iTunes里查到。然后增加一个iOS App Development的Certificate和Provisioning Profile:
最后得到一个证书文件,和一个.mobileprovision后缀的文件,就是Provisioning Profile文件了。安装证书文件,将.mobileprovision文件通过Xcode导入设备中。一般连接iso设备时Xcode会自动弹出,也可以在Xocde菜单的 Help - Xcode Help - Devices修改Provisioning Profile文件。下图是在Apple开发者中心创建的Provisioning Profile:
要保证App ID和程序的Target Properies - Bundle identifier的值相同,否则会报错Code Sign error: A valid provisioning profile matching the application’s Identifier ‘org.dmyz.app’ could not be found。
Generate PEM
切换到左侧导航菜单的Certificates,找到之前生成的APNs Development iOS证书,点击[Download]按钮下载,得到aps_development.cer。安装后,在钥匙串访问中找到对应的证书。接下来要分别导出证书和专用密钥。选中Apple Development IOS Push Services: org.dmyz.app上点击右键,选择[导出Apple Development IOS Push Services: org.dmyz.app],文件格式切换成.cer,导出为cert.cer。点击左侧▶展开它,可以看到名为dmyz,类别为[专用密钥]的这个子项,选中导出为key.p12。
导出这两个文件后,用openssl进行处理:
$ openssl pkcs12 -in key.p12 -out key.pem -nodes
会要求输入Import Password,如果导出时没有设置则为空。我在SO还找到另一种方法:
$ openssl pkcs12 -nocerts -out key.pem -in key.p12
$ cat cert.pem key.pem > key.pem
主要差别是key没有加nodes参数,最后把两个文件合并了。测试过可以通过。执行第二条命令时需要输入PEM pass phrase,至少4个字符。
现在得到key.pem和cert.pem文件了,无论服务是NodeJS, Python还是Ruby,这只需要这两个文件。
Coding
通知计数、点击通知执行的函数等细节设置和Location notification相同,区别是要指定Device。Xcode中打开项目的AppDelegate.m文件中增加一个函数,来获取设备Token:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@"deviceToken: %@", deviceToken);
}
最后,安装apn,把key.pem和cert.pem拷贝到项目目录。执行以下代码可以推送通知到指定设备,截图是当程序在后台运行且屏幕锁定时收到通知的效果:
var apn = require('apn');
var token = 'a788a70f...79ecc6145'; //长度为64的设备Token
var options = { "gateway": "gateway.sandbox.push.apple.com" },
apnConnection = new apn.Connection(options),
device = new apn.Device(token),
note = new apn.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 60;
note.badge = 3;
note.alert = "动漫驿站 \n点击查看更新的1篇文章";
note.payload = {'messageFrom': 'Caroline'};
apnConnection.pushNotification(note, device);