此教程更新于17年8月,之后可能会有变动。
此教程基于development
环境,生产环境雷同。
证书申请
创建AppID
1.1 用于推送的AppID必须是确定的bundleID,不能带有通配符。
1.2 appid创建之后,push状态处于待激活状态
从证书颁发机构请求request证书
2.1 申请证书之前,必须先申请request证书
申请development证书
3.1
3.2 此时需要上传之前申请到的request证书
申请Provisioning Profiles
4.1 绑定刚才已创建的appID
4.2 绑定刚才已申请的development
证书
4.3 绑定设备,继续直到done。
申请SSL推送证书
5.1 申请此证书有两种方式
- 1.1 选中之前创建的AppID,点击编辑按钮
1.2 点击下面的创建SSL证书
1.3 仍然需要上传request证书
1.4 生成的SSL证书
- 2.1 先删除第一种方法生成的SSL推送证书。直接点击创建开发环境证书
2.2 选择创建SSL
2.3 此时比第一种方法多出的一个步骤就是绑定AppID
2.4 之后的步骤同第一种方法
下载已经申请的文件
6.1 下载开发证书cer文件
6.2 下载开发推送证书cer文件
6.3 下载开发Provisioning Profiles文件
6.4 下载的文件如下:
6.5 双击安装证书文件和描述文件
移动端代码
本例基于iOS10以上代码测试,实际开发中需要做出兼容。
1.1 iOS10以上SDK提供最新UNUserNotificationCenter
接口,调用此接口,需提前导入#import <UserNotifications/UserNotifications.h>
1.2 代码如下:
#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate ()<UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//消息推送注册
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
[center setDelegate:self];
UNAuthorizationOptions type = UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert;
[center requestAuthorizationWithOptions:type completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
DLog(@"注册成功");
}else{
DLog(@"注册失败");
}
}];
[application registerForRemoteNotifications];
return YES;
}
//完成注册后收到devicetoken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
//上传deviceToken给后台服务器
NSString *fullStr = [deviceToken description];
DLog(@"fullstr= %@",fullStr);
NSString *deviceTokenStr = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"deviceTokenStr:\n%@",deviceTokenStr);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
DLog(@"注册推送失败:%@",error);
}
//处理通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
DLog(@"接收到推送内容==%@", response.notification.request.content.userInfo);
}
服务端配置
生成pem
文件
1.1 服务端上传给APNS的服务器需要pem
格式的文件,所以,先转换格式。
1.2 从钥匙串中导出文件
- 导出cert.cer文件,其实就是推送证书
aps_development.cer
文件
- 导出cert.p12文件
- 导出key.p12文件,需要输入验证密码
1.3 有两种可用的转换方式
- a-1. 导出的
cert.cer
文件和导出的key.p12
文件配对转换pem格式。转换key.p12的时候用到导出时候的密码
$ openssl x509 -in cert.cer -inform DER -outform PEM -out cert.pem
$ openssl pkcs12 -in key.p12 -out key.pem -nodes
//得到cert.pem和key.pem
//知道 cert.cer文件其实就是之前导出的推送证书,所以用```aps_development.cer```替换```cert.cer```也是可行的。
- a-2. 导出的
cert.p12
文件和导出的key.p12
文件配对转换。
$ openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12
$ openssl pkcs12 -nocerts -out key.pem -in key.p12
//注意点:此种方法转换key.p12的时候,需要输入passphrase密码。记住此密码,在JS请求参数中需要使用。
现在得到的key.pem
和cert.pem
文件,无论服务是NodeJS, Python还是Ruby,都可以使用。
验证生成的pem
文件是否可用
2.1 终端输入如下代码:
$ openssl s_client -connect gateway.sandbox.push.apple.com:443 -cert cert.pem -key key.pem
显示如下说明可用
Key-Arg : None
Start Time: 1502948067
Timeout : 300 (sec)
Verify return code: 0 (ok)
Node.js代码
3.1 首先要安装apn
模块
//查看所有版本
$ npm view apn versions
//安装指定版本
$ npm view apn@2.1.5
3.2 js具体代码: 方式1和方式2都可以实现
/// <reference path="../NodeJS/typings/index.d.ts" />
"use strict"
const apn = require("apn");
//方式1
/*
let deviceToken = "f8ca917e82724992d387210c0cd7dd65381d818f6b6e8387e1fdfe9d6e77f410";
var options = {
cert: "cert.pem",
key: "key.pem",
// passphrase: "123456",
production: false
};
var apnProvider = new apn.Provider(options);
var note = new apn.Notification();
// note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 3;
note.sound = "ping.aiff";
note.alert = "You have a new message";
note.payload = {'messageFrom': 'John Appleseed'};
note.topic = "com.longhuiyun.ios";
console.log(`sending: ${note.compile()} to ${token}`);
apnProvider.send(note, deviceToken).then( result => {
console.log("sent: ", result.sent.length);
console.log("failed: ",result.failed.length);
console.log(result.failed);
});
apnProvider.shutdown();
*/
//方式2
/**/
let tokens = ["43fe62877980297b1a1da9043fdb70246272fe635855f174d714bc1301da445a",
"f8ca917e82724992d387210c0cd7dd65381d818f6b6e8387e1fdfe9d6e77f410"];
let service = new apn.Provider({
cert: "cert.pem",//注意填入正确的文件路径
key: "key.pem",
passphrase: "123456", //创建pem的时候确定的passphrase,需要加入
gateway: "gateway.sandbox.push.apple.com",//可忽略
port: 443 //可忽略
});
let note = new apn.Notification({
alert: "测试下",
sound: "ping.aiff",
});
note.topic = "com.longhuiyun.ios";
console.log(`sending: ${note.compile()} to ${tokens}`);
service.send(note, tokens).then( result => {
console.log("sent: ", result.sent.length);
console.log("failed: ",result.failed.length);
console.log(result.failed);
});
//关闭服务
service.shutdown();
3.3 在终端中执行js文件,会看到消息发送成功
$ node apnsService.js
path=== ./key.pem
sending: {"aps":{"alert":"测试下","sound":"ping.aiff"}} to 43fe62877980297b1a1da9043fdb70246272fe635855f174d714bc1301da445a,f8ca917e82724992d387210c0cd7dd65381d818f6b6e8387e1fdfe9d6e77f410
sent: 2 //发送成功
failed: 0
[]
3.4 最后可以写个web页面,专门负责可视化信息发送
3.5 对于服务端和APNS服务端通信可能出现的错误码
此出处为苹果官方文档 具体错误可以参考。
3.6 对于apn
模块的使用,具体可参考官方git