应用启动时,调用registerUserNotificationSettings:
方法与Apple的APNS
服务器通信,并注册推送服务。注册成功后,系统会调用应用的didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
方法。其中,deviceToken
参数是APNS
返回的deviceToken
(用来唯一标识一个设备,每个设备上的每个应用的deviceToken
都不相同)。在这里,我们需要将这个deviceToken
上传到我们自己的服务器中,用来向指定设备发送push消息。
服务端拿到这个deviceToken
后,使用我们申请好的证书和这个deviceToken
,向Apple的APNS
服务器发送一个JSON串,这个JSON串中包含我们要向iOS设备发送的内容。
APNS
服务器收到这个JSON串,并向deviceToken
指定的设备发送消息,iOS应用收到消息后,会调用- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
方法。
APNS
发送完消息后,将消息是否推送成功的状态反馈给我们的服务端。
这里的准备主要是需要从Apple开发者中心注册好应用ID并申请相应的证书。
CertificateSigningRequest.certSigningRequest
的文件。CertificateSigningRequest.certSigningRequest
文件。 aps_development.cer
和`CertificateSigningRequest.certSigningRequest
两个文件。aps_development.cer
证书,在钥匙串访问中会出现一个Apple Development IOS push services证书,一个公用密钥和一个专用秘钥,秘钥的名称与证书助理中填写的名称一致。如果服务端使用PHP
开发,则需要在终端中执行以下命令生成PHP
需要的pem
证书
openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
生成pem证书时需要输入导出证书时设置的密码。
然后执行下面的命令,将两个证书合成为一个:
cat apns-dev-cert.pem apns-dev-key.pem > apns-dev.pem
最终的apns-dev.pem
就是服务端所需要的证书。
如果服务端使用C#
或JAVA
开发,则需要生成p12
格式的证书:
openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
openssl pkcs12 -export -in apns-dev-cert.pem -inkey apns-dev-key.pem -certfile CertificateSigningRequest.certSigningRequest -name "push" -out push.p12
生成.p12
证书的过程同样需要输入导出证书时设置的密码。
最终生成的push.p12
证书就是服务端要使用的证书。
在Member Center左侧选择Provisioning Profiles栏下的All,然后点击右上角的加号。
出现下面的页面后,选择iOS App Devleopment项(这里也是开发测试配置文件,实际产品可以选择Distribution栏下的项目),点击Continue。
在选择设备页面中选择Select All,(如果你的设备不在列表中,需要先在Member Center中添加你的设备),点击Continue。
在出现界面的Provisioning Profiles栏中找到刚才创建的Provisioni Profiles名字,点击后面的Download按钮下载到本机。
首先,需要在客户端申请推送权限并获取Device Token
,在AppDelegate.m
中加入如下代码:
//应用启动完毕
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIUserNotificationSettings *notiSetting = [UIUserNotificationSettings
settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound)
categories:nil];
//申请远程推送权限
[application registerUserNotificationSettings:notiSetting];
return YES;
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings
{
//注册远程推送
[application registerForRemoteNotifications];
}
//成功申请到远程推送权限后将会调用这个方法
//早这个方法中需要将获取到的device token上传到自己的服务器端
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
//upload this device token to your service for send message to this device
NSLog(@"Device Token : %@",deviceToken);
}
//注册远程推送失败后会执行这个方法
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error
{
NSLog(@"Error : %@", [error userInfo]);
}
//收到远程推送时会执行下面的方法
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
{
NSString *serinfo = [NSString stringWithFormat:@"%@",userInfo];
NSLog(@"User info : %@", serinfo);
}
当应用第一次运行时就会弹出下面的提示框询问用户是否同意应用推送消息给他。
用户选择“好”后,didRegisterForRemoteNotificationsWithDeviceToken:
就会被调用,从而获得device token
。
需要注意的是,当应用正在执行时收到推送通知时会直接调用didReceiveRemoteNotification:
方法。
当应用不在执行时,收到通知后点击通知消息冷启动后会首先调用didFinishLaunchingWithOptions:
再调用didReceiveRemoteNotification:
方法。
当冷启动时,在didFinishLaunchingWithOptions:
方法中也可以直接使用下面的代码获取推送过来的消息(与didReceiveRemoteNotification:
中传入的userInfo
相同)
NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
为了省事,我这里使用了javapns库,代码如下:
public class Puser
{
public static void main(String[] args)
{
//device token
String token = "d8e5c7668df7c9572ae575afb23c0cec03c0921ab5547a3df6dbb15fdde3541e";
//证书路径
String certPath = "cert/push.p12";
//证书密码
String passWord = "5555555";
try
{
PushNotificationPayload payLoad = PushNotificationPayload.complex();
//要推送的消息
payLoad.addCustomAlertBody("Hello");
//应用图标显示的角标
payLoad.addBadge(1);
//收到推送到达时的声音
payLoad.addSound("default");
PushedNotifications notifications = Push.payload(payLoad, certPath, passWord, false, token);
System.out.println(notifications);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
执行后收到通知:
上面我们曾说到我们要给用户推送消息,要向Apple的服务器发送一个JSON串,这个JSON串就叫做Payload
。
Payload
是一个指定格式的JSON串,Apple规定Payload
中必须包含一个名为aps
的Dictionary
属性,并且Payload
的大小不能超过2k,否则,APNs
将拒绝转发这个Payload
。下面的表格列出了aps
中的各个属性。
APNs
只是“尽最大努力”将Payload
交付给用户而不能够保证一定会将Payload
交付给用户
aps
的属性列表:
键 | 值类型 | 备注 |
---|---|---|
alert | String 或 Dictionary | 要显示的消息 |
bage | Number | 应用图标角标的数字 |
sound | String | 通知声音 |
alert
属性值的类型为String
时,消息到达时显示默认样式的提示框(带关闭和查看按钮),类型为Dictionary
时,各属性参考Apple消息推送服务开发指南。
例如,下面就是一个标准的Payload
:
{
"aps" : {
"alert" : "You got your emails.",
"badge" : 1,
"sound" : "default"
}
}
当然,我们也可以在Payload
中加入自定义的属性,方便客户端根据服务器的推送做出相应的响应。
例如,下面就是一个添加了自定义属性的Payload
:
{
"aps" : {
"alert" : "You got your emails.",
"badge" : 1,
"sound" : "default"
},
"acme1" : "bar",
"acme2" : 42
}
其中,acme1
和acme2
都是自己定义的属性。
自定义的属性必须为JSON的原生属性,例如
String
、Array
、Dictionary
等
当然,为了方便实现应用的国际化,Payload
中也添加了一些实现应用国际化的字段,这些都可以去查阅Apple的Apple消息推送服务开发指南,这里只是对Payload
的一个简介。