当前位置: 亚洲城ca88 > ca88 > 正文

iOS推送布告,APNS的推送机制

时间:2019-09-15 04:11来源:ca88
参考文档:Local and Remote Notification Programming Guide 与Android上我们自己实现的推送服务不一样,Apple对设备的控制非常严格,消息推送的流程必须要经过APNs: 极光推送: 1.JPush当前版本是

参考文档:Local and Remote Notification Programming Guide

与Android上我们自己实现的推送服务不一样,Apple对设备的控制非常严格,消息推送的流程必须要经过APNs:

极光推送:

1.JPush当前版本是1.8.2,其SDK的开发除了正常的功能完善和扩展外也紧随苹果官方的步伐,SDK在iOS8刚推出的时候跟进更新。在一定程度上来讲,能够体现极光推送的技术力量是比较强大的;

2.应用内消息提高了推送服务的及时性和可靠性,在、紧随当下追求应用实时性的潮流,能够提供更好的推送服务的用户体验;

3.JPush除了有着完善的控制台外,还提供了服务端的接口,方便开发者的服务器调用,方便开发者对于应用管理、整合即通过应用服务器极光服务器推送服务器用户设备,简化了服务器端的开发强度,也方便运营期间发送推送消息的流程;

4.如果开发者同时需要用户统计和推送功能,那么这是一款不容错过的SDK因为JPush提供了全面直观的统计数据,在控制台中以图表的形式呈现,方便运营期间的数据分析;

5.当前版本的SDK仅有5M左右的体积,嵌入应用或者打包应用影响都是很小的,开发者大可不必担心。

6.对于高级用户还开放了富媒体推送和更多的API调用次数,当然这也需要开发者付出一定的费用,对于有需要的开发者可以自行了解。

综合评价:

笔者在测试极光推送之前也测试过其他的推送服务,在项目中使用的也是极光推送。对比这些推送服务来讲,极光是从文档、SDK简洁程度、集成难度、服务都较为满意的。在笔者的应用中有需要统计用户行为的需求,而极光也恰恰满足了这种需求,带来了极大的方便。总而言之,极光推送确实能给开发者带来不错的编程体验。

通过笔者的测试,极光SDK的集成难度不大,文档注释清晰易懂。而且有较为强大的技术支持(QQ群、论坛、博客)。所以对于有一定开发经验的需要使用推送服务、本地推送服务、推送统计的开发者是非常适用的,而即使是对推送要求不高的项目,极光SDK的开发成本也并不是很高,所以具有较为宽泛的适用范围。

极光推送是推送服务商中较大的一个,目前的用户量也相当庞大,其文档、注释、网站控制台都较为完善。极光SDK对推送服务进行高级封装,除了实现APNS推送以外还实现了基于TCP连接的应用内消息,在一定程度上提高了推送服务的可靠性。其SDK非常简洁,简化了集成过程,而通过阅读API的注释及相关文档,开发者可以很快的掌握SDK的使用。对于有一定开发经验的开发者是非常易于上手的

高送达率,时效保证:极光推送自主协议确认推送消息的送达。开发者可以灵活的自定义推送时间

服务集成SDK简单:简单复制便可直接编译运行

推送内容多样性服务:除了通知,还有消息、多媒体等

使你的应用程序保持高注意力:即使用户没有打开应用程序,极光推送也能够推送通知到达用户手机

自定义消息内容:JPushSDK把内容完全转给应用程序,由开发者应用程序去处理自定义消息

接入即可用功能:客户端集成SDK即可享受高效 专业的推送服务,操作简单的管理后台

多平台:同时支持 Android与iOS平台

高安全性:传输信道加密,推送数据自主加密

高稳定性,大容量,高并发:目前极光推送平台支持 十亿级用户,高达20万/秒的下行速度

省流量,低耗电功能强大:待机流量消耗20K/天,电 量消耗30mAh/天  毕竟国外网络服务在中国都颇为不完善,而且推送服务对网络和服务器端有较高的要求。因此,国内很快就兴起数家像极光推送云消息推送服务的公司,是国内做得较好的一家。

优势:

1)   开放注册,免费向所有的开发者开放使用

2)SDK流量电量消耗很少

3)集成简单,很快就能够集成跑起来

4)服务器端推送支持大并发量、延迟小

目前「用户消息提醒(User Notifications)」包括两个类型,分别为

图片 1

友盟推送:

1.推送形式多样:开发者可以在网页设置或者采用API接入方式进行消息推送,并可以选择文本消息、应用更新以及json式消息,满足开发者不同场景下的运营需求。

2.用户分组灵活:开发者可以使用多个预置条件或者自定义的用户标签把用户分组,对每个分组的用户推送更有针对性的消息,满足开发者定向推送的需求。

3.设备能耗极低:SDK中采用了先进的长链接多路复用以及其他优化方案,将用户设备的电量和流量消耗控制在最低水平,确保用户体验最优。

4.建立与用户直接沟通的通道:不论是新品上架还是精彩活动举行,都可以实时的推送到用户设备,让用户第一时间获取到相关信息。适时而准确的消息推送,可以大幅度提升用户的活跃度和忠诚度。

功能特色

用户分群推送

开发者可以使用多个预置条件或者自定义的用户标签把用户分群,对每个用户群的用户推送更有针对性的消息,满足开发者定向推送的需求。

接入方式灵活

开发者可以通过网站WEB界面或者API接入方式进行消息推送,且支持开发者提交已分群的用户ID到友盟服务器进行消息推送,满足开发者不同场景下的运营需求。

服务质量高

实现了同一设备多应用共享一个长连接、智能心跳等优化方案。消息发送速度快,长连接稳定,设备能耗低。

优点:

友盟推送的SDK包很小,集成的时候不需要导入其它库,集成后对应用影响很小,使用方便;

在网站的应用信息中,可以看到推送的历史记录:包括发送总数、用户打开数、打开率等等,方便统计

友盟推送集成是最简单,使用也最方便,推送的渠道也是多样的。

缺点:

但是在同网络环境下,友盟的推送速度却是比不上极光推送的。而服务器端的api使用不太方便,需要设置一个服务器IP地址才能使用。自定义的字数最多只有1500字

  • 本地消息提醒(Local Notification)
  • 远端消息推送提醒(Push Notification)

articlex.png

百度推送:

1.Push 服务

Push 服务初始化及绑定

Push 服务解除绑定

2.Tag 管理

创建 tag

删除 tag

列出 tag

3.通知推送

4.推送效果反馈

百度有延迟,推送不稳定,不人性化,很多也只是为了服务百度系的公司来用。到达率存在问题。开发者网站不是非常用户友好。

百度推送,推送Android还行,在推送ios时推送内容有限制,官方说是不能大于4k,但是实际推送的内容80个汉字以上就不行,推送不了。

优点:

1.1、 推送及时,支持推送通知、穿透消息推送、富媒体消息推送。

1.2、 提供常见问题解答。

1.3、 提供客服支持。

缺点:

1.1、 与其他推送平台相比官网没有详细的开发文档,不方便开发者查阅。

1.2、 与其他推送平台相比,集成困难。

建议:在官网放一份详细的开发文档,方便开发者查阅和集成。

其中「本地消息提醒」是指 App 在运行时添加到设备的消息推送计划,设置好推送时间、推送内容和展现形式等,然后添加到系统的消息推送列表,到预订的时间即会显示在消息栏;而「远端消息推送提醒」则是由远端服务器推送消息,App 在运行时先注册好远端推送服务,获取设备的推送 token ,并将 token 返回给服务端,服务端在需要推送消息给这个设备时,将推送内容并携带此 token 和其他验证信息发送给「苹果消息推送服务」,再由其推送给用户端设备。

这里 Provider 是指某个应用的Developer,当然如果开发者使用AVOS Cloud的服务,把发送消息的请求委托给我们,那么这里的Provider就是AVOS Cloud的推送服务程序了。上图可以分为三步:
第一步:AVOS Cloud推送服务程序把要发送的消息、目的设备的唯一标识打包,发给APNs。
第二步:APNs在自身的已注册Push服务的应用列表中,查找有相应标识的设备,并把消息发送到设备。
第三步:iOS系统把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知

iOS推送简介:

在移动应用中,推送已经成为不可缺少的重要功能。本文档主要介绍有关 iOS 推送的基础知识,并从几个典型问题出发,分析如何解决在实现推送中出现的一些问题。

1.1 本地通知和远程通知简介

在 iOS 设备上(模拟器无法使用推送),系统收到通知后这样处理:

在屏幕上弹出一些选项,或者在屏幕顶部显示横幅(banner)如下图左

App 的角标数值发生变化,具体表现为 App icon 右上角的小红点及数字,如邮件中的红点

伴随推送消息的提示声音

当应用处于前台运行时,系统是不会在屏幕上显示通知,但是仍会调用相应的 API。

只有真机可以使用推送功能。

用户可以设置每一个 App 的通知权限,如下图最后一个:

用户可以选择关闭某个应用的推送功能。还可以设置通知是否在通知中心显示、通知到达时是否发出声音、通知能否改变 App 角标以及锁屏时是否显示该 App 的通知,还可以设置通知到达时的提醒样式。当然,这些都可以分别对每一个应用进行单独设置。

1.1.1 本地通知

本地通知是一种基于时间的提醒方式。

本地通知最多向系统注册 64 个,当超过这个数量后,最早注册的本地通知会被丢弃。

本地通知在 iOS 设备上的显示与远程推送通知一样。

1.1.2 远程通知

iOS App 运行在后台时,无法主动进行网络连接。

远程通知可以用来提醒用户。发送远程通知时,服务器首先需要使用推送证书与 APNs(Apple Push Notification service)建立安全连接,然后将消息传递给它。当 APNs 收到消息后,会通过与手机之间的长连接下发到对应的手机上,然后 iOS 弹出这条消息来提醒用户。

用户看通过点击或滑动通知来运行 App。可通过程序中相应的方法可以获取通知信息,然后做相应的逻辑处理。

如果不是通过通知启动 App,那么无法在程序中获取通知信息。例如通过点击应用图标启动 App。

推送通知都包含 Payload:一个 Apple 已经定义好的属性列表,操作系统根据它决定使用哪一种方式来提醒用户。还可以在 Payload 中加入一些自定义数据。

通知并不能一定到达。如果在用户无法收到推送通知时(关机或网络不可用),这种情况下 APNs 收到了多条发往这台设备的通知,那么只会保留最后一条,最早的会被丢弃。

APNs 首先通过移动蜂窝网络发送通知,只有当移动蜂窝网络不可用时才使用 Wi-Fi。

1.2 通知的两种推送环境

在使用 iOS 远程推送功能时,有两种不同的环境。开发环境(Development)以及生产环境(Production)。

App 当前使用的推送环境与 Xcode - Build Settings - Code Signing - Provisioning Profile 文件的模式一致。

1.2.1 证书与证书校验

与 APNs 之间是加密的连接,因此需要使用证书来加密连接。每个的推送环境有自己单独的推送证书,即开发证书和生产证书。

在将证书最终转为 pem 格式后,可通过与 APNs 连接来测试证书是否有效。

开发环境:

openssl s_client -connectgateway.sandbox.push.apple.com:2195-cert MyApnsDev.pem

生产环境:

openssl s_client -connectgateway.push.apple.com:2195-cert MyApnsPro.pem

当输入完命令回车后,终端首先会输出很多相关信息。

当连接建立失败时,会直接 close 掉。

当连接建立成功时,终端会停止输出,并等待你输入,你可以随便输入一些字符后摁回车,然后连接才会关闭。

以上命令在 Mac 下没有问题,在其他操作系统下需要指定服务器当前的 CA 根证书,具体可以从网站上下载:

entrust_2048_ca.cer 下载

下载完成之后,在以上命令后面加上 -CAfile entrust2048ca.cer 即可

1.2.2 DeviceToken

通知需推送到具体某一台设备,而 DeviceToken 就是这台设备的标识符。在向 APNs 发送推送通知时,需要使用 DeviceToken 来指定这条通知将要到达的设备 。

每一台设备,不同的推送环境下分别有一个 DeviceToken。即 DeviceToken 也分开发环境和生产环境。

应用在向 APNs 注册推送通知时,会根据当前 Xcode 工程中 target 对应的 Provisioning Profile 决定请求对应环境的 DeviceToken。即系统返回的 DeviceToken 其环境与 Provisioning Profile 的环境对应。

一般在使用 Xcode 开发时,Provisioning Profile 为 Development ,因此获取的 DeviceToken 也是开发模式。

在 App 需要打包为 ipa 文件或者需要上传到 AppStore 时,会将 Provisioning Profile 文件修改为 Distribution,这时获取到的 DeviceToken 是生产模式。

1.3 在应用程序中注册远程推送功能

App 必须要向 APNs 请求注册以实现推送功能,在请求成功后,APNs 会返回一个设备的标识符即 DeviceToken 给 App,服务器在推送通知的时候需要指定推送通知目的设备的 DeviceToken。在 iOS 8 以及之后,注册推送服务主要分为四个步骤:

使用registerUserNotificationSettings:注册应用程序想要支持的推送类型

通过调用registerForRemoteNotifications方法向 APNs 注册推送功能

请求成功时,系统会在应用程序委托方法中返回 DeviceToken,请求失败时,也会在对应的委托方法中给出请求失败的原因。

将 DeviceToken 上传到服务器,服务器在推送时使用。

上述第一个步骤注册的 API 是 iOS 8 新增的,因此在 iOS 7,前两个步骤需更改为 iOS 7 中的 API。

DeviceToken 有可能会更改,因此需要在程序每次启动时都去注册并且上传到你的服务器端。

注意:iOS 设备与 APNs 需要建立一条长连接,之后的推送注册以及推送获取都需要通过这条长连接。

当 iOS 设备无网络可用时,在向 APNs 请求注册后,既不会有注册成功的回调,也不会有注册失败的回调。如果发生这种情况,需要检查设备的网络连接。

开发环境和生产环境建立不同的长连接,且设备上要至少有一个开发环境的 App 并且注册推送,才会建立开发环境下的长连接。

当已经注册过推送服务后,以后系统会立刻返回给你 DeviceToken。还有一点是,返回 DeviceToken 的回调方法并不一定是只有在你注册或者再次注册时才被系统调用,当DeviceToken 改变时也会被系统直接调用。

1.4 在程序中处理通知

我们来看一下当系统传递本地或远程通知的时候都有哪些情况。

通知到达时,应用不在前台

这种情况下,操作系统将呈现通知,弹出一个选项或者在屏幕上部显示 banner 提醒,给 App 角标设置对应的数字,可能播放提示音,当用户稍微往下滑动通知时显示更多的 action 按钮(如果有设置)。

通知到达时,应用在前台运行

这种情况下,不会弹出 banner。系统会根据当前的通知是本地通知还是远程通知,调用不同的方法,并且在方法中传递通知的 Payload 数据。

在 iOS8 的通知中,用户点击通知上的自定义 action 按钮进入应用。

这种情况下,系统会调用 iOS8 中对 action 按钮新增的处理方法。

用户点击通知进入应用。

系统会根据当前的通知是本地通知还是远程通知,调用不同的方法,并且在方法中传递通知的 Payload 数据。

注意:只有当通过通知进入到 App 时,才可以获取到通知信息。

1.4.1 自定义通知提示音

你可以在 App 的 Bundle 中加入一段自定义提示音文件。然后当通知到达时可以指定播放这个文件。必须为以下几种数据格式:

Linear PCM

MA4(IMA/ADPCM)

μLaw

aLaw

你可以将它们打包为aiff、wav或caf文件。自定义的声音文件时间必须小于 30 秒,如果超过了这个时间,将被系统声音代替。

1.4.2 Payload

Payload 是通知的一部分,每一条推送通知都包含一个 Payload。它包含了系统提醒用户通知到达的方式,还可以添加自定义的数据。即通知主要传递的数据为 Payload。

Payload 本身为 JSON 格式的字符串,它内部必须要包含一个键为aps的字典。aps 中可以包含以下字段中的一个或多个:

alert:其内容可以为字符串或者字典,如果是字符串,那么将会在通知中显示这条内容

badge:其值为数字,表示当通知到达设备时,应用的角标变为多少。如果没有使用这个字段,那么应用的角标将不会改变。设置为 0 时,会清除应用的角标。

sound:指定通知展现时伴随的提醒音文件名。如果找不到指定的文件或者值为 default,那么默认的系统音将会被使用。如果为空,那么将没有声音。

content-available:此字段为 iOS 7 silent remote notification 使用。不使用此功能时无需包含此字段。

推送通知跟NSNotification有所区别:

1> NSNotification是抽象的,不可见的

2> 推送通知是可见的(能用肉眼看到)

iOS中提供了2种推送通知: 本地推送通知, 远程推送通知

1> 本地推送通知(Local Notification)

2> 远程推送通知(Remote Notification)

推送通知可以不让在前台运行的app,告知app内部发生了什么变化,比如:有新的内容,新消息等.

推送通知的使用:

发出推送通知时,如果当前程序正运行在前台,那么推送通知就不会被呈现出来

点击推送通知后,默认会自动打开发出推送通知的app

不管app打开还是关闭,推送通知都能如期发出

推送通知有5种呈现效果:

  1. 在屏幕顶部显示一块横幅(显示具体内容)

  2. 在屏幕中间弹出一个UIAlertView(显示具体内容,使用较少)

  3. 在锁屏界面显示一块横幅(锁屏状态下,显示具体内容)

  4. 播放音效(提醒作用)

  5. 更新app图标的数字(说明新内容的数量)

推送通知分为本地推送通知和远程推送通知,下面一一介绍.

本地推送通知

本地推送通知不需要服务器的支持,不需要联网就可以发送通知.通常本地推送通知用于定时提醒用户,比如清理垃圾,淘宝购物,纪念日提醒等任务.

在苹果官方给出了本地推送通知的一些属性,以及使用.

属性介绍:

@property(nonatomic,copy)NSDate *fireDate;// 设置本地推送的时间@property(nonatomic,copy) NSTimeZone *timeZone;// 时区(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)@property(nonatomic) NSCalendarUnit repeatInterval;// 没隔多久重复发出一次@property(nonatomic,copy)NSCalendar *repeatCalendar;// 设置日期@property(nonatomic,copy) CLRegion *region NS_AVAILABLE_IOS(8_0);// 比如某一个区域的时候发出通知@property(nonatomic,assign)BOOL regionTriggersOnce NS_AVAILABLE_IOS(8_0);// 进入区域是否重复@property(nonatomic,copy)NSDictionary *userInfo;// 附加的额外信息@property(nonatomic,copy)NSString *alertBody;// 消息的内容@property(nonatomic)BOOL hasAction;// 是否显示alertAction的文字(默认是YES)@property(nonatomic,copy)NSString *alertAction;// 设置锁屏状态下,显示的一个文字@property(nonatomic,copy)NSString *alertLaunchImage;// 启动图片@property(nonatomic,copy)NSString *soundName;//  UILocalNotificationDefaultSoundName@property(nonatomic)NSInteger applicationIconBadgeNumber;// 应用图标右上角的提醒数字@property(nonatomic,copy)NSArray *scheduledLocalNotifications;// 获得被调度(定制)的所有本地推送通知(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)

使用方法:

创建本地通知

UILocalNotification *localNoti = [[UILocalNotification alloc] init];

调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)

[[UIApplication sharedApplication]scheduleLocalNotification:localNoti];取消调度本地推送通知

-(void)cancelLocalNotification:(UILocalNotification *)notification;-(void)cancelAllLocalNotifications;

立即发出本地推送通知

-(void)presentLocalNotificationNow:(UILocalNotification *)notification;

注意:

iOS8.0以后在本地推送通知上新加了一些新功能,为了用户体验,以及更加人性化,如果要使用本地通知,需要得到用户的许可.

需要在AppDelegate中添加如下代码

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {/*    UIUserNotificationTypeNone    = 0,      没有,没有本地通知    UIUserNotificationTypeBadge  = 1 << 0, 接受图标右上角提醒数字    UIUserNotificationTypeSound  = 1 << 1, 接受通知时候,可以发出音效    UIUserNotificationTypeAlert  = 1 << 2, 接受提醒(横幅/弹窗)    */// iOS8需要添加请求用户的授权if ([UIDevice currentDevice].systemVersion.floatValue >=8.0) {        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];

[application registerUserNotificationSettings:settings];

}

}

点击本地推送通知

当用户点击本地推送通知,会自动打开app,这里有2种情况

1> app并没有关闭,一直隐藏在后台(运行在后台)

让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)

-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

1

1

2> app已经被关闭(进程已死)

启动app,启动完毕会调用AppDelegate的下面方法

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象

若要实现界面的跳转,需要分清当前应用程序的所处状态,进行判断:

1> 若是当前应用在后台运行,接收到通知时,要想进行界面的跳转,可以在didReceiveLocalNotification:方法中实现跳转界面的方法

2> 若是当前的应用程序已经关闭,我们在前面说到,当应用关闭,推送通知也会如期发送.但此时,是不会走didReceiveLocalNotification:方法的,那我们只有didFinishLaunchingWithOptions:方法利用,launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象,实现跳转的功能.

下面是演示代码:

#import"AppDelegate.h"@interfaceAppDelegate ()@end@implementationAppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {/*    UIUserNotificationTypeNone    = 0,      没有,没有本地通知    UIUserNotificationTypeBadge  = 1 << 0, 接受图标右上角提醒数字    UIUserNotificationTypeSound  = 1 << 1, 接受通知时候,可以发出音效    UIUserNotificationTypeAlert  = 1 << 2, 接受提醒(横幅/弹窗)    */// iOS8需要添加请求用户的授权if ([UIDevice currentDevice].systemVersion.floatValue >=8.0) {        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];        [application registerUserNotificationSettings:settings];    }if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {// 跳转界面    }returnYES;}/** *  如果应用在后台,通过点击通知的时候打开应用会来到该代理方法 *  如果应用在前台,接受到本地通知就会调用该方法 * *  @param notification 通过哪一个通知来这里 */- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{if (application.applicationState == UIApplicationStateActive)return;if (application.applicationState == UIApplicationStateInactive) {// 实现跳转    }}- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{returnYES;}#import"ViewController.h"

@interfaceViewController ()// 点击按钮之后添加通知- (IBAction)addLocalNote;@end@implementationViewController- (void)viewDidLoad {    [super viewDidLoad];    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];}- (IBAction)addLocalNote {// 1.创建本地通知UILocalNotification *localNote = [[UILocalNotification alloc] init];// 设置什么时间弹出    localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];// 设置弹出的内容    localNote.alertBody = @"您有新消息";// 设置锁屏状态下,显示的一个文字    localNote.alertAction = @"快点打开";// 是否显示alertAction的文字(默认是YES)    localNote.hasAction =YES;// 设置音效    localNote.soundName = UILocalNotificationDefaultSoundName;// 应用图标右上角的提醒数字    localNote.applicationIconBadgeNumber =1;// 设置UserInfo来传递信息    localNote.userInfo = @{@"alertBody" : localNote.alertBody, @"applicationIconBadgeNumber" : @(localNote.applicationIconBadgeNumber)};// 2.调度通知    [[UIApplication sharedApplication] scheduleLocalNotification:localNote];

}

远程推送通知

远程推送通知:就是通过网络从远程服务器推送给客户端的通知.

为什么需要远程推送通知?

传统获取数据的局限性

只要用户关闭了app,就无法跟app的服务器沟通,无法从服务器上获得最新的数据内容

远程推送通知可以解决以上问题

不管用户打开还是关闭app,只要联网了,都能接收到服务器推送的远程通知

远程推送通知的使用

所有的苹果设备,在联网状态下,都会与苹果的服务器建立长连接

长连接: 只要联网了,就一直建立连接

长连接的作用: 时间校准, 系统升级, 查找我的iPhone

长连接的好处 : 数据传输速度快 , 数据保持最新状态

远程推送功能机制

苹果给iOS和Mac添加了消息推送的功能,使得我们可以通过后台服务器给应用程序(APP)发送消息,不管APP是否正在使用,比如邮箱的来件提示功能。这项服务被称为Apple Push Notification service(APNs)。里面一共涉及到四个角色:APP、设备、APNs和应用后台服务器(Provider),其中APP、后台服务器和APNs之间使用deviceToken唯一的标识一个用户。

推送服务的工作流程:

APP向系统注册推送服务。

设备从APNs请求deviceToken。

通过代理方法将deviceToken返回给APP。

APP将deviceToken发送给应用后台服务器(Provider)。

应用后台服务器保存deviceToken,然后在需要推送通知的时候,给APNs发送信息,使用deviceToken标识所要送达的客户端。

APNs将后台服务器发过来的数据推送到设备。

设备将消息分发给应用程序。

在使用推送功能的时候,需要在开发者中心创建支持Push Notification的证书,并且将证书和私钥用于应用后台服务器与APNs之间通信。

我也写了一个比较易懂的远程推送流程图http://blog.csdn.net/ismilesky/article/details/48324723

远程推送近年来,都是通过第三方进行实现,因为第三方推送的功能比较强大.

推送平台: 百度推送 , 极光推送, 腾讯信鸽推送, 个推

远程推送我们这里以极光推送(第三方)为例,进行测试.

极光远程推送的使用

如要使用极光第三方远程推送,我们需要集成iOS SDK,需要进行应用的配置,和环境配置.在开发者中心有

developer.apple.com开发者账号 , iOS真机(iPhone、iPad、iPod)等。

远程推送应用配置过程:

创建支持远程推送功能的App ID

申请开发者证书,并选中刚刚创建的App ID

下载CER文件,并导入钥匙串管理

申请发布证书,并选中刚刚创建的App ID

下载CER文件,并导入钥匙串管理

检查App ID,确认证书已经指定

这些相关配置极光推送已经给了非常详细的文档资料, iOS SDK集成指南 :http://docs.jpush.io/guideline/ios_guide/, iOS SDK调试指南 :http://docs.jpush.io/client/ios_tutorials/#ios-sdk, 只需要按照流程进行就可以.

实现代码:

#define kDeviceVersion  ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)#import"AppDelegate.h"#import"APService.h"@interfaceAppDelegate ()@end@implementationAppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// Override point for customization after application launch.#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1if (kDeviceVersion) {//可以添加自定义categories        [APService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:nil];    }else {//categories 必须为nil        [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];    }#else//categories 必须为nil    [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];#endif    [APService setupWithOption:launchOptions];returnYES;}#pragma mark - 获取device token (必须实现)// 当得到苹果的APNs服务器返回的DeviceToken就会被调用- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{NSLog(@"%@",deviceToken);// Required    [APService registerDeviceToken:deviceToken];}#pragma mark - 获取device token失败- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{NSLog(@"function == %s  line == %d  error == %@",__FUNCTION__,__LINE__,error);}// 接收到远程通知,触发方法和本地通知一致 (必须实现)- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {// Required    [APService handleRemoteNotification:userInfo];}#pragma mark - 使用后台的远程消息推送 (必须实现)/** 1> 在Capabilities中打开远程推送通知 2> 实现该代理方法远程消息数据格式: {"aps" : {"content-available" : 1},"content-id" : 42} 执行completionHandler有两个目的 1> 系统会估量App消耗的电量,并根据传递的UIBackgroundFetchResult 参数记录新数据是否可用 2> 调用完成的处理代码时,应用的界面缩略图会自动更新 注意:接收到远程通知到执行完网络请求之间的时间不能超过30秒 if (userInfo) { int contentId = [userInfo[@"content-id"] intValue]; ViewController *vc = (ViewController *)application.keyWindow.rootViewController; [vc loadDataWithContentID:contentId completion:^(NSArray *dataList) { vc.dataList = dataList; NSLog(@"刷新数据结束"); completionHandler(UIBackgroundFetchResultNewData); }]; } else { completionHandler(UIBackgroundFetchResultNoData); }*/- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {// IOS 7 Support Required    [APService handleRemoteNotification:userInfo];    [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];// 判断应用程序在前台还是后台if (application.applicationState == UIApplicationStateActive) {// 活跃状态// 实现方法    }elseif (application.applicationState == UIApplicationStateInactive) {// 不活跃状态// 实现方法    }else {// application.applicationState == UIApplicationStateBackground// 后台// 实现方法    }/** 必须回调 */    completionHandler(UIBackgroundFetchResultNewData);}测试时,运行完应用程序,要发送通知,进行推送的测试,真机收到通知才算成功.

下面详细描述一下两者的编程逻辑:


无论是「本地消息提醒」还是「远端消息推送提醒」,首先是要向系统注册消息提醒服务,经用户允许后才能推送和显示消息提醒。代码如下:

为了实现消息推送,有两点非常重要:
1,App的推送证书
要能够完整实现一条消息推送,需要我们在App ID中打开Push Notifications,需要我们准备好Provisioning Profile和SSL证书,并且一定要注意Development和Distribution环境是需要分开的。最后,把SSL证书导入到AVOS Cloud平台,就可以尝试远程消息推送了。具体的操作流程可以参考我们的使用指南:iOS推送证书设置指南。
2,设备标识DeviceToken
知道了谁要推送,或者说要推送给哪个App之后,APNs还需要知道推到哪台设备上,这就是设备标识的作用。获取设备标识的流程如下:

 UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];

图片 2

其中关键的是UIUserNotificationSettings settingsForTypes: categories:,第一个参数表示的是推送消息的形式,表示当前 App 所有的推送中都包含哪些形式,可选类型包括:

articlex1.png

  • UIUserNotificationTypeNone
  • UIUserNotificationTypeBadge :设置图标的红点未读消息数量
  • UIUserNotificationTypeSound :发出消息提示声音
  • UIUserNotificationTypeAlert :弹出消息提醒

第一步:App打开推送开关,用户要确认TA希望获得该App的推送消息
第二步:App获得一个DeviceToken
第三步:App将DeviceToken保存起来,这里就是通过[AVInstallation saveInBackground]将DeviceToken保存到AVOS Cloud
第四步:当某些特定事件发生,开发者委托AVOS Cloud来发送推送消息,这时候AVOS Cloud的推送服务器就会给APNs发送一则推送消息,APNs最后消息送到用户设备

可使用 | 组合成一个 UIUserNotificationType ,表示具有多种类型;第二个参数 categories 表示自定义的可交互提醒分类组,后文会做详细解释,如果无需交互的消息提醒,设置为 nil 即可。


以上代码在 App 内第一次执行时,会让用户选择是否允许消息提醒,选择结果会通过 application:didRegisterUserNotificationSettings 方法通知 App ,此后每次执行,则会直接将用户的配置选择结果传递给此方法(因为用户可以在「设置」中更改设置,所以不一定是第一次选择的结果)。

推送相关的几个概念

可以通过调用[[UIApplication sharedApplication] currentUserNotificationSettings]获取当前用户对于当前 App 的消息推送设置

iOS 10 开始,使用 UNNotificationSettings 代替 UIUserNotificationSettings ,旧版 iOS 还得继续使用 UIUserNotificationSettings

消息类型

一条消息推送过来,可以有如下几种表现形式:
1.显示一个alert或者banner,展现具体内容
2.在应用icon上提示一个新到消息数
3.播放一段声音
开发者可以在每次推送的时候设置,在推送达到用户设备时开发者也可以选择不同的提示方式。

完成消息注册后,就可以在需要的地方提交消息提醒计划,示例代码如下:

本地消息通知

iOS上有两种消息通知,一种是本地消息(Local Notification),一种是远程消息(Push Notification,也叫Remote Notification),设计这两种通知的目的都是为了提醒用户,现在有些什么新鲜的事情发生了,吸引用户重新打开应用。
本地消息什么时候有用呢?譬如你正在做一个To-do的工具类应用,对于用户加入的每一个事项,都会有一个完成的时间点,用户可以要求这个To-do应用在事项过期之前的某一个时间点提醒一下TA。为了达到这一目的,App就可以调度一个本地通知,在时间点到了之后发出一个Alert消息或者其他提示。
我们在处理推送消息的时候,也可以综合运用这两种方式。


 UILocalNotification *localNotif = [[UILocalNotification alloc] init]; // 设置推送时间 localNotif.fireDate = date; localNotif.timeZone = [NSTimeZone localTimeZone]; // 如果是每个间隔循环提醒,可设置 localNotif.repeatInterval = 30; // 消息主体 localNotif.alertBody = @"Notize Me"; // 用于 iWatch localNotif.alertTitle = @"Notize Me"; // 设置小红点未读数量 localNotif.applicationIconBadgeNumber = 1; // 设置提示声音 (默认声音在模拟器中不发声) localNotif.soundName = UILocalNotificationDefaultSoundName; // 设置附带数据 localNotif.userInfo = @{@"name": @"Mayday"}; // 提交消息推送计划 (可以使用`presentLocalNotificationNow:`立刻推送) [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];

代码里面如何实现推送

以上代码被执行后,在预订的时间,此消息将被触发。 如果消息触发时

首先,我们要获取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也会失效。


  • App 在运行且位于前台:不会在系统的消息列表显示,而是直接触发 application:didReceiveLocalNotification
  • App 当时在运行但不位于前台:在消息栏显示提醒,用户点击消息后再触发 application:didReceiveLocalNotification
  • App 当时没有在运行:也会在消息栏显示提醒,用户点击消息后,会启动 App ,会在 didFinishLaunchingWithOptions: 中携带提醒信息(launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]),然后再触发 application:didReceiveLocalNotification

其次,我们要处理收到消息之后的回调

我们可以设想一下消息通知的几种使用场景:
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];
    }
}

每个 App 在每台设备上只允许有 64 个计划推送的本地消息,重复的消息只按一个算。iOS 10 使用 UNNotificationRequest 代替 UILocalNotification

常见问题FAQ

取消本地消息提醒

如果发现之前提交的消息提醒计划(无论是已经触发的或是尚未触发的)已经不再需要,则可取消,避免给用户造成困扰。

  1. 通过 [[UIApplication sharedApplication] currentUserNotificationSettings] 获取所有已经提交的消息提醒;
  2. 通过 userInfo 识别出需要取消的目标消息;
  3. 使用 [[UIApplication sharedApplication] cancelLocalNotification:notice]; 取消目标消息。

如果取消的提醒已经触发并显示在消息栏,则取消后会将提示栏的消息一并删除。如果想取消所有消息,则可使用 [[UIApplication sharedApplication] cancelAllLocalNotifications]

除了要进行上文提到的「注册消息推送」外,还要注册远端消息推送获取设备的 token 用于服务器推送时区别每个设备。如果注册成功,则将返回的 DeviceToken 发送给自己的后端服务器,用于向 APNs 推送消息(后端推送提醒消息的部分内容不在此文中描述)。示例代码如下:

 - didFinishLaunchingWithOptions:(UIApplication *)app { // 其他代码.... // 注册消息推送 UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert; UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings]; // 注册远端消息推送 [[UIApplication sharedApplication] registerForRemoteNotifications]; } // 以下两个方法用于处理远端消息推送服务注册的反馈结果: // 注册成功时,此方法被触发 - application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:devToken { const void *devTokenBytes = [devToken bytes]; self.registered = YES; [self sendProviderDeviceToken:devTokenBytes]; // 自定义方法,用于将 token 发送给后端服务器 } // 注册失败时,此方法将被触发 - application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { NSLog(@"Error in registration. Error: %@", err); }

远端消息提醒的处理:

  • App 在运行且位于前台:不会在系统的消息列表显示,而是直接触发 application:didReceiveRemoteNotification:fetchCompletionHandler(如果没有实现以上方法,则调用application:didReceiveRemoteNotification:));
  • App 当时在运行但不位于前台:在消息栏显示提醒,用户点击消息后再触发 application:didReceiveRemoteNotification:fetchCompletionHandler
  • App 当时没有在运行:也会在消息栏显示提醒,用户点击消息后,会启动 App ,会在 didFinishLaunchingWithOptions: 中携带提醒信息(launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]),然后再触发 application:didReceiveRemoteNotification:fetchCompletionHandler

同一个设备的 DeviceToken 会因各种原因被改变(恢复备份、重装 App等),所以每次 App 启动后需要重复以上步骤注册远程消息推送(建议在 didFinishLaunchingWithOptions 方法内),

iOS 8 后增加了一种带操作的消息提示,也就是在消息上提供可操作的按钮(在消息提示栏上是2个,在锁屏界面上可最多4个),与普通的消息区别是增加了一个 categories示例步骤如下:

第一步:创建消息按钮动作(以下为创建一个的示例),至少需要2个按钮动作。

 UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init]; // The identifier that you use internally to handle the action. acceptAction.identifier = @"ACCEPT_IDENTIFIER"; // 按钮显示标题 acceptAction.title = @"Accept"; // 用户点击消息时,是否要将 App 调至前台 acceptAction.activationMode = UIUserNotificationActivationModeBackground; // 是否以高亮背景显示,以表示此操作具有毁灭性 acceptAction.destructive = NO; // Indicates whether user authentication is required to perform the action. acceptAction.authenticationRequired = NO;

第二步:创建一个消息分类

 // First create the category UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init]; // Identifier to include in your push payload and local notification inviteCategory.identifier = @"INVITE_CATEGORY"; // Set the actions to display in the default context [inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault]; // Set the actions to display in a minimal context [inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];

第三步:创建一个消息分类组,并将这个组添加到消息设置

 NSSet *categories = [NSSet setWithObjects:inviteCategory, nil]; UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:categories]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings];

第四步:配置消息

  • 本地消息,在设置消息前,配置category为对应的分类名称,如:notification.category = @"INVITE_CATEGORY";
  • 远端消息,设置category字段为对应的分类名称
{ "aps" : { "alert" : "You’re invited!", "category" : "INVITE_CATEGORY" }}

第五步:按上文提到的方式推送消息

我能推送长消息吗

不能,APNs限制了每个notification的payload最大长度是256字节,超长的消息是不能发送的。

消息处理

  • 点击了消息本身:按不带操作的方式处理
  • 点击了操作按钮
    • 本地消息:application:handleActionWithIdentifier:forLocalNotification:completionHandler:
    • 远端消息:application:handleActionWithIdentifier:forRemoteNotification:completionHandler:

使用[identifier isEqualToString: @"ACCEPT_IDENTIFIER"]判断按钮,然后在根据 notification 处理。处理完最后要调用completionHandler()

-- EOF --

如何显示本地化的消息

有两种办法可以实现推送消息的本地化:
1,在推送的payload中使用loc-key和loc-args来指定进行本地化,这样Provider方只需要按照统一的格式来发送即可,消息的解析和组装则由客户端来完成。
2,如果推送的payload里面不包含loc-key/loc-args信息,那么Provider方就需要自己做本地化处理,然后给不同的device发送不同的消息,为了做到这一点,还需要app在上传device token的时候也把用户的语言设置信息传回来。
目前,因为AVOS Cloud主要就是瞄准中国大陆市场和海外中文用户,所以我们在推送上还不提供多语言支持。

编辑:ca88 本文来源:iOS推送布告,APNS的推送机制

关键词: 亚洲城ca88