常常有同窗问咱们,iOS上推送究竟怎么作啊,为何个人设备总收不到推送呢,这里跟你们集中讨论一下iOS上推送的实现细节。json
APNS的推送机制缓存
与Android上咱们本身实现的推送服务不同,Apple对设备的控制很是严格,消息推送的流程必需要通过APNs:服务器
这里 Provider 是指某个应用的Developer,固然若是开发者使用AVOS Cloud的服务,把发送消息的请求委托给咱们,那么这里的Provider就是AVOS Cloud的推送服务程序了。上图能够分为三步:微信
第一步:AVOS Cloud推送服务程序把要发送的消息、目的设备的惟一标识打包,发给APNs。网络
第二步:APNs在自身的已注册Push服务的应用列表中,查找有相应标识的设备,并把消息发送到设备。app
第三步:iOS系统把发来的消息传递给相应的应用程序,而且按照设定弹出Push通知ide
为了实现消息推送,有两点很是重要:工具
1,App的推送证书开发工具
要可以完整实现一条消息推送,须要咱们在App ID中打开Push Notifications,须要咱们准备好Provisioning Profile和SSL证书,而且必定要注意Development和Distribution环境是须要分开的。最后,把SSL证书导入到AVOS Cloud平台,就能够尝试远程消息推送了。具体的操做流程能够参考咱们的使用指南:iOS推送证书设置指南。spa
2,设备标识DeviceToken
知道了谁要推送,或者说要推送给哪一个App以后,APNs还须要知道推到哪台设备上,这就是设备标识的做用。获取设备标识的流程以下:
第一步:App打开推送开关,用户要确认TA但愿得到该App的推送消息
第二步:App得到一个DeviceToken
第三步:App将DeviceToken保存起来,这里就是经过[AVInstallation saveInBackground]将DeviceToken保存到AVOS Cloud
第四步:当某些特定事件发生,开发者委托AVOS Cloud来发送推送消息,这时候AVOS Cloud的推送服务器就会给APNs发送一则推送消息,APNs最后消息送到用户设备
推送相关的几个概念
消息类型
一条消息推送过来,能够有以下几种表现形式:
1. 显示一个alert或者banner,展示具体内容
2. 在应用icon上提示一个新到消息数
3. 播放一段声音
开发者能够在每次推送的时候设置,在推送达到用户设备时开发者也能够选择不一样的提示方式。
本地消息通知
iOS上有两种消息通知,一种是本地消息(Local Notification),一种是远程消息(Push Notification,也叫Remote Notification),设计这两种通知的目的都是为了提醒用户,如今有些什么新鲜的事情发生了,吸引用户从新打开应用。
本地消息何时有用呢?譬如你正在作一个To-do的工具类应用,对于用户加入的每个事项,都会有一个完成的时间点,用户能够要求这个To-do应用在事项过时以前的某一个时间点提醒一下TA。为了达到这一目的,App就能够调度一个本地通知,在时间点到了以后发出一个Alert消息或者其余提示。
咱们在处理推送消息的时候,也能够综合运用这两种方式。
代码里面如何实现推送
首先,咱们要获取DeviceToken。
App须要每次启动的时候都去注册远程通知——经过调用UIApplication的registerForRemoteNotificationTypes:方法,传递给它你但愿支持的消息类型参数便可,例如:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // do some initiale working ... [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; return YES; }
若是注册成功,APNs会返回给你设备的token,iOS系统会把它传递给app delegate代理——application:didRegisterForRemoteNotificationsWithDeviceToken:方法,你应该在这个方法里面把token保存到AVOS Cloud后台,例如:
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSLog(@"Receive DeviceToken: %@", deviceToken); AVInstallation *currentInstallation = [AVInstallation currentInstallation]; [currentInstallation setDeviceTokenFromData:deviceToken]; [currentInstallation saveInBackground]; }
若是注册失败,application:didFailToRegisterForRemoteNotificationsWithError:方法会被调用,经过NSError参数你能够看到具体的出错信息,例如:
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"注册失败,没法获取设备ID, 具体错误: %@", error); }
请注意:注册流程须要在app每次启动时调用,这并不不会带来额外的负担,由于iOS操做系统在第一次得到了有效的device token以后,会本地缓存起来,之后app再调用registerForRemoteNotificationTypes:的时候会马上返回,并不会再进行网络请求。另外,app层面不该该对device token进行缓存,由于device token也有可能变化——若是用户重装了操做系统,那么APNs再次给出的device token就会和以前的不同,又或者是,用户restore了原来的backup到新的设备上,那么原来的device token也会失效。
其次,咱们要处理收到消息以后的回调
咱们能够设想一下消息通知的几种使用场景:
1,在app没有被启动的时候,接收到了消息通知。这时候操做系统会按照默认的方式来展示一个alert消息,在app icon上标记一个数字,甚至播放一段声音。
2,用户看到消息以后,点击了一下action按钮或者点击了应用图标。若是action按钮被点击了,系统会经过调用application:didFinishLaunchingWithOptions:这个代理方法来启动应用,而且会把notification的payload数据传递进去。若是应用图标被点击了,系统也同样会调用application:didFinishLaunchingWithOptions:这个代理方法来启动应用,惟一不一样的是这时候启动参数里面不会有任何notification的信息。
示例代码以下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // do initializing works ... if (launchOptions) { // do something else ... [AVAnalytics trackAppOpenedWithLaunchOptions:launchOptions]; } [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; return YES; }
3,若是远程消息发送过来的时候,app正在运行,这时候会发生什么呢?
app代理的application:didReceiveRemoteNotification:方法会被调用,同时远程消息中的payload数据会做为参数传递进去。
示例代码以下:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { if (application.applicationState == UIApplicationStateActive) { // 转换成一个本地通知,显示到通知栏,你也能够直接显示出一个alertView,只是那样稍显aggressive:) UILocalNotification *localNotification = [[UILocalNotification alloc] init]; localNotification.userInfo = userInfo; localNotification.soundName = UILocalNotificationDefaultSoundName; localNotification.alertBody = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"]; localNotification.fireDate = [NSDate date]; [[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; } else { [AVAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo]; } }
常见问题FAQ
1. 我能推送长消息吗?
不能,APNs限制了每一个notification的payload最大长度是256字节,超长的消息是不能发送的。
2. 推送怎么加声音提醒?
消息推送是能够指定声音的。譬如你能够对正面的反馈使用欢快的声音,对负面的反馈使用低沉一点的声音,均可以达到别出心裁让人眼前一亮的目的。
你须要先放一些aiff、wav或者caf音频文件到app的资源文件中,而后在推送的时候指定不一样的音频文件名就能够了。
3. 推送的Badge是怎么回事?
推送并不必定会致使应用图标上红色数字增长,是否显示这一数字,显示成多少,都取决于开发者本身。
在发送推送消息的时候,咱们能够选择是否递增这一数字;若是不选择这一项,那么消息推送并不会致使应用图标上红色数字的出现。
好,如今问题来了,这个数字若是搞出来了,怎么让它消失掉呢?
其实咱们只须要在任什么时候候设置 UIApplication.applicationIconBadgeNumber 属性为0,就可让这个数字消失掉。
通常咱们会选择在应用启动的时候(application:didFinishLaunchingWithOptions:方法中),或者干脆一点,在应用每次被切换到前台的时候(applicationWillEnterForeground:方法中),调用这一行代码,便可马上清除掉Badge数字了。
4. AVOS Cloud平台发出去的通知格式到底是什么样子的
对于每一条推送消息,都包含一个payload,一般是组成了一个JSON的Dictionary,这其中必不可少的是aps属性,它对应的value也是一个Dictionary,包含下面一些内容:
1)alert消息(文本或Dictionary)
2)应用图标上的红色数字
3)播放的声音文件名
在由推送激活的app打开事件中,application:didFinishLaunchingWithOptions:的options参数就是这个大的Dictionary对象。
{ aps = { alert = "hello, everyone"; badge = 2; sound = default; }; }
这里要注意的时alert部分,它的值能够是一个String(文本消息),也能够是一个JSON的Dictionary。当它是文本消息的时候,系统就会把这些文字显示到一个alertview中;若是它也是由一个JSON Dictionary组成的话,其格式以下:
* body
* action-loc-key
* loc-key
* loc-args
* launch-image
body部分就是alertView中将要展示出来的文本消息,loc-属性主要是用来实现本地化消息,launch-image只是app主bundle里的一个图片文件的名称,通常来讲咱们不指定这一属性。
5. 如何显示本地化的消息
有两种办法能够实现推送消息的本地化:
1)在推送的payload中使用loc-key和loc-args来指定进行本地化,这样Provider方只须要按照统一的格式来发送便可,消息的解析和组装则由客户端来完成。
2)若是推送的payload里面不包含loc-key/loc-args信息,那么Provider方就须要本身作本地化处理,而后给不一样的device发送不一样的消息,为了作到这一点,还须要app在上传device token的时候也把用户的语言设置信息传回来。
目前,由于AVOS Cloud主要就是瞄准中国大陆市场和海外中文用户,因此咱们在推送上还不提供多语言支持。
6. 应用该怎么响应推送消息
上面说的处理流程,只能简单展现一下远程消息,激活用户让他们从新回到app中来。可是有时候,咱们但愿带给用户更好的使用体验,譬如若是咱们告诉用户:张三刚刚评论了你的照片。这时候用户若是点击action按钮进入app,咱们是展现具体的评论页面为好,仍是展现一般的启动页面而后让用户本身去找张三的评论好?我想负责任的开发者都会选择前者。
要作到灵活响应不一样类型的通知消息,咱们须要在通知的payload中增长更多信息,而不能仅仅只有alert出来的文字信息。对于AVOS Cloud消息推送平台来说,就须要开发者使用更高级功能的JSON格式。譬如咱们发送这样的json字符串{"action":{"type":4},"alert":"hello, everyone”} 最终在app内会收到这样的UserInfo Dictionary:
{ action = { type = 4; }; aps = { alert = "hello, everyone"; badge = 4; }; }
“hello, everyone”会显示到alertView中,可是整个Dictionary会经过launchOptions传递给application: didFinishLaunchingWithOptions: 方法,这样咱们在程序里面就能够对不一样的消息实现不一样的跳转了。
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各类精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocostudio开发工具包的最新动态及培训信息。关注微信能够第一时间了解最新产品和服务动态,微信在手,天下我有!