做者:陈裕发, 腾讯系统测试工程师
商业转载请联系腾讯WeTest得到受权,非商业转载请注明出处。
原文连接:http://wetest.qq.com/lab/view/380.htmlhtml
本文主要对iOS Push的在线push、本地push及离线(远程)push进行梳理,介绍了相关逻辑,测试时要注意的要点以及相关工具。小小的Push背后蕴藏着大大的逻辑!前端
在线push:当用户在线(APP在前台)时,收到的状态栏的消息提醒,称为在线push。这个功能与苹果系统无关,是咱们本身的APP开发的一种功能,该push与设置中是否打开“通知”无关。ios
这里以iOS Qzone为例,当APP在前台时,本身发的说说被点赞了,收到的在线push以下:git
Qzone在线pushgithub
离线push:当APP在离线(kill掉进程、切到后台、锁屏)时,收到的消息提醒,称为离线push。离线push是须要通过苹果的APNs服务器才能够推送到某台设备的某个APP上的,这是和本地push的本质区别。push与设置中是否打开“通知”有关。服务器
这里最简单的以你们经常使用的手机QQ为例,当APP在后台、锁屏或者被kiil了进程时,收到了消息:app
离线push
一、静默push框架
静默push用的场景不较少,这里只作简要介绍。ide
首先咱们看看离线(远程)push与静默push的区别:
普通离线(远程)push:收到推送后(有文字有声音),点开通知,进入APP后,才执行-- (void)application:(UIApplication didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void result))handler )application )userInfo (^)(UIBackgroundFetchResult函数
静默push:收到推送(没有文字没有声音),不用点开通知,不用打开APP,就能执行(void)application:(UIApplication )application)userInfo didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void (^)(UIBackgroundFetchResultresult))handler,用户彻底感受不到。
因此静默push又被咱们称作 Background Remote Notification(后台远程推送)。静默推送是在iOS7以后推出的一种推送方式。它与其余推送的区别在于容许应用收到通知后在后台(background)状态下运行一段代码,可用于从服务器获取内容更新。
本地push:本地推送和远程推送的功能是同样的,都是要提醒用户去作某些事情。可是和远程推送不一样的就是本地推送是不须要设备联网的,而远程推送是必须要设备联网的,由于只有联网状态下,才能和苹果的APNs服务器创建长链接,从而推送消息。本地推送是由App本身设定的,而且发送给安装此App的这台设备,属于一对一的对应关系。比较典型的应用是闹钟相似的场景。该push与设置中是否打开“通知”有关。
最容易看到本地push的场景,能够直接在手机设置一个计时器,计时器时间到了就会弹出本地push:
本地push
因为本地push原理和做用相对于在线push和离线push都更为简单明了,下文主要介绍在线push和离线push。
试验过iOS10之前的本地push方法在iOS10+的系统也能使用,不过可能有些参数不生效。
一、当即展现( iOS10之前)
本地push稍微简单,有两种方式能够调用,一种是presentLocalNotificationNow方法,当即展现本地push:
二、延迟展现( iOS10之前)
另外一种是用scheduleLocalNotification方法按计划来弹本地推送:
若是使用这种方法,须要对推送的时间进行设置,举个例子,设为5秒后:
其中alertBody是消息内容锁屏与不锁屏时效果以下:
本地push效果
applicationIconBadgeNumber是消息数量,咱们能够看到这里设置为66:
消息数
一、 App没有启动状况下处理本地push
这种状况下,当点击通知时,会启动App,而在App中,开发人员能够经过实现AppDelegate中的方法:- (BOOL)application:(UIApplication)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,而后从lauchOptions中获取App启动的缘由,如果由于本地通知,则能够App启动时对App作对应的操做,比方说跳转到某个画面等等。
二、App运行在后台及前台
上面的2种状况的处理基本一致, 不一样点只有当运行再后台的时候,会有弹窗提示用户另一个App有通知,对于本地通知单的处理都是经过AppDelegate的方法:- (void)application:(UIApplication )application didReceiveLocalNotification:(UILocalNotification *)notification来处理的。
iOS10之后,本地通知能够由使用 UNUserNotificationCenter来管理。
建立方法:
接下来须要需建立一个包含待通知内容的 UNMutableNotificationContent 对象:
在iOS上能够经过如下几种触发器来触发本地push:
● UNCalendarNotificationTrigger 传送本地通知的日期和时间。
● UNTimeIntervalNotificationTrigger 传递本地通知以前必须过时的时间。
● UNLocationNotificationTrigger 用户必须达到的地理位置才能提供本地通知。
● UNPushNotificationTrigger 表示通知是从Apple推送通知服务发送的对象。
假如以时间间隔(TimeInterval)来触发,则设置触发器代码为:
推送本地push的代码为:
在线push相对简单,由于是内部实现,具体流程如上面所示。
一、判断app是否在线
此处能够根据APP自身的后台策略如上一次与后台交互的时间等方法来判断APP是否在线或者离线。认为在线,会发送在线push,不然,发送离线push。
二、在线push特色
● 在线push有如下几个特色:
● 不须要通过苹果APNs。
● 须要本身实现长连接。
● 代码在app内部实现。
离线push流程
主要流程为:
● 服务器端将消息先发送到苹果的APNs
● 由苹果的APNs将消息推送到客户的设备端
● 由iOS系统将接收到的消息传递给相应的App。
简而言之离线push是苹果系统的行为,与app状态无关,可以直接推送到指定手机的指定app。
在进一步了解离线push前,咱们有必要先了解几个名词。
一、离线push名词解释
—APNs
APNs:Apple Push Notification service(苹果推送通知服务)。
APNs主要用于如下场景:当用户主动杀掉 APP,或者 APP 进入后台超过约定时长时,APP会被kill,这样保障了前台 APP 的流畅性,也延长了手机的使用时长,得到了较好的用户体验,可是这也意味着,服务器没法主动和用户交互(如推送实时消息等),因此苹果推出了 APNs,容许设备和服务器分别与苹果的推送通知服务器保持长链接状态。
关于APNs的更新有如下几点:
● iOS 8之后,APNs推送的字节是2k,iOS8之前是256字节
● iOS 9之后APNs支持HTTP/2协议栈,优化长链接,具备标准的HTTP返回和管道复用技术
● iOS 10之后,推送的字节是4k,APNs可根据推送消息的惟一标示符查询某条消息是否被用户阅读,可更新某一推送消息,而不用发重读的多条消息
关于APNs更全面的介绍能够看官方文档:
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1
—payload
什么是payload?对于每一条发送给APNs的推送消息,都包含一个payload,一般是组成了一个JSON的Dictionary,这其中必不可少的是aps属性,它对应的value也是一个Dictionary,包含一些但不限于如下内容:标题、副标题、内容、附件、category等,如
—device token
什么是device token?咱们看一下官方的简介:
device token: APNs uses device tokens to identify each unique app and device combination. It also uses them to authenticate the routing of remote notifications sent to a device.(device token是APNs用于区分识别每一个iOS设备和设备上不一样app的一个标识符,还能够用于APNs经过它将推送消息路由到指定设备上)
即:device token里包含了device id和bundle id的信息,可是device id和bundle id不会肯定惟一的device token。
可是,这里有个坑,查资料得知,iOS8及以前的iOS系统,对于同一部手机,若是卸载后重装APP的话,device token是不会变的,在token变了之后,老的token,就被认为是无效了,苹果不会对这部分无效的token推送。可是,对iOS9及之后的iOS系统,对于同一部手机,卸载后重装APP的device token是会发生变化的,并且老的token不会无效,还能够正常推送,这应该是苹果的一个bug,可是苹果也没有修复这个问题,因此这个须要开发者本身来解决,不然容易出现一个app收到多个push的问题。官方的说法是:
To protect user privacy, do not use device tokens to identify user devices. Device tokens change when the user updates the operating system and when a device’s data and settings are erased. As a result, apps should always request the current device token at launch time.(即此举为了保护用户隐私,device token会在更新系统、擦除设置重置后变化,在必定时间后会过时)
二、离线push详细流程
知道了以上概念后咱们从新来看一下离线(远程)push的详细流程:
离线push详细流程
1) 首先是应用程序注册消息推送。
2) iOS跟APNS Server要deviceToken。应用程序接受deviceToken。
3) 应用程序将deviceToken发送给PUSH服务端程序。
4) 服务端程序向APNS服务发送消息。
5) APNS服务将消息发送给iPhone应用程序。
值得注意的是,当因为用户反复卸载重装程序(虽然几率很小)等缘由致使多个device Token指向同一台设备的同一个app,又把多个device Token发给APNs时,用户就会收到多条push。苹果APNs是不会对多个device Token是否指向同一台设备的同一个app作校验的,因此须要后台来作去重等处理保证用户不会收到多条push。
一、iOS 7以上对离线(远程)push时的响应
iOS 7以上关于接受离线push有两个函数
那么这两个函数有什么区别呢?其实这两个方法都是用来处理离线push的。
差异就是,若是app在前台是收到离线(远程)push,那么就会调用
相对的,若是在后台或者杀进程状况下,点击收到的离线push,那么就会调用,若是没有实现
则会调用
若实现了前者,就只调用前者。
二、iOS 10以上对离线(远程)push的响应
iOS10对push的处理主要增长了两个方法
其中前者是对APP在前台时收到push时的处理,后者是点击push进入APP执行的函数。
用得比较多的是后者,咱们能够举个例子,点击push进入APP后如何获取push的消息、角标、标题等内容:
iOS10新增的UserNotifications框架,主要有了这样几方面的更新:
● 用UserNotifications框架替换了原先与通知相关的接口,通知文字可分为title、subtitle和body三部分,通知可携带附件
● 系统在展现通知以前,能够唤起app附带的service extension,而且容许它改动通知的内容
● 用户在对通知右滑查看、下拉或者3d touch的时候,通知会展开,展开后页面的布局能够由app附带的content extension来决定
iOS10之前的push只有文字,甚至没有标题。
iOS10之后的push更加多样化,能够有主标题,副标题,甚至还有附件,这里以我司的腾讯新闻为例(有标题,内容,和附件):
腾讯新闻push
3D touch点入详情之后:
腾讯新闻push详情
这里咱们惊奇的发现,除了能够携带图片这样的附件、push还能展开详情之外,进入详情之后,下面还多了“打开”、“收藏”、“不感兴趣”这些选项,这里就涉及到如下iOS10的新特性。
由于payload有大小限制,因此若是remote notification想要携带附件,那么payload上只能带上如附件下载地址之类的信息,等通知到达客户端后由service extension下载附件到本地,而后在初始化UNNotificationAttachment对象时传入附件在本地的URL。
初始化UNNotificationAttachment对象时,能够传入option参数。这里的option参数能够强制指定附件的类型,能够选择是否展现缩略图,以及缩略图截取自附件的哪一帧、哪一部分。
目前iOS10通知只将几种格式的图片、音频和视频做为附件,附件的大小也有必定限制,具体能够看官方文档中的限制说明。
关于附件的更加详细的说明,能够参考官方文档:
https://developer.apple.com/d...
上面提到的“打开”、“收藏”、“不感兴趣”这些选项其实就是push携带的action,其实从iOS8开始,通知已经能够携带action了。而在iOS10中,通知的action被放在了更明显的位置,与action相关的接口也有了很大变化。
决定一个通知应该有哪些action呢?在payload中,这是由category字段决定的。若是咱们但愿一个通知能携带若干个action,咱们就须要将若干个action和一个category绑定起来。通知到达前端后,系统会根据category的名字来决定要给这个通知展现哪些action:
怎么得知用户选了哪一个action并作出相应操做呢?这须要给UNUserNotificationCenter指定一个delegate:
而后在delegate的类中实现
方法:经过response.notification.request.content.categoryIdentifier和response.actionIdentifier就能够得知用户选择的action了。
这里主要讲应用的比较多的离线(远程)push的改变push方法
一、改变本地push内容
本地push,只要request的id同样,那么就能够更新推送:
更新的例子:
此外,还有删除全部推送等,都在UNUserNotificationCenter.h中实现。
二、改变离线(远程)push内容
目前远程push只支持更新push内容,更新须要经过新的字段apps-collapse-id来做为惟一标示。方法是在HTTP/2 请求头中使用相同的apns-collapse-id,这样收到一样的apns-collapse-id的push时,push内容便会更新。
使用场景:比较容易理解的一个场景就是球赛比分,好比如今是1:0,若是变成1:1的话,只须要刷新原来的新闻,这样用户就不会由于同一场比赛收到多条push。
有两个与push相关的extension,可能咱们会好奇这两个extension有什么不一样,为何须要两个?它们分别实现什么功能呢?
push相关extension
一、notification service extension
给app添加notification service extension后,系统会在收到通知后唤醒它,并容许它修改通知的内容,以后再展现这个通知。
service extension只对remote notification起做用,local notification是没法唤起它的。
若是想要让系统唤起service extension的话,payload必须符合这样几个条件:
1) 必须增长mutable-content字段并为1,这表示容许客户端修改这个通知:
payload(举例)以下:
2)这个通知必须展现一个alert,若是只是一个修改badge的通知的话,是不会唤起service extension的
3)静默推送是不能唤起service extension的,因此payload中不能有”content-available” : 1字段
因此,经过这个notification service extension,你能够在接收到推送以后、展现推送以前处理一些事情,好比说更新一下推送内容,或者在后台作一些其余事情。
二、notification content extension
另外一项notification content extension用于彻底自定义推送展开后的视图。上面腾讯新闻的展开后的视图就是经过这个notification content extension实现的。
依然以腾讯新闻为例子:
展开界面
这里Notification Content Extension大展拳脚的地方,在这里能够自定义绘制不一样的内容,将但愿展示给用户的额外信息能够加载这里。
下半部分的notification action的实现就是在上面提到的“携带action的通知”。
Q:离线push,支持角标(badge)在本地角标数值上+1这样的操做吗?
A:不支持。若是是本身实现push服务的话,须要本身的后台将角标值badge发送个APNs服务器,有些APP使用第三方push SDK除外。
Q:若是重复收到离线push,多是什么状况?
A:
1)iOS9以后卸载重装后生成新的deviceToken,后台对多个deviceToken都发送了push
2)后台对注销了的帐号也发送了push。
总而言之通常是后台的逻辑出现了问题,而不是APNs服务器出现问题。
Q:直接卸载APP,还能收到离线push吗?
A:不会收到。直接卸载APP,虽而后台不知道APP被卸载了,仍然会对以前的帐号发送push,可是因为手机上没有对应APP,因此并不会收到push。
Q:为何有时候全新安装APP就立马有红点角标?
A:这是由于卸载该APP时有红点角标。每一个 APP 的角标都是存在 iOS 手机系统里的,开发没法修改,因此此时卸载前有角标,从新安装也会有角标。可是,APP 卸载以后超过一天的时间再重装,那么角标就会被系统清空,届时也不会有新安装的 APP 就有角标的状况存在。
Knuff离线push工具下载连接:https://github.com/KnuffApp/Knuff/releases
使用方法也比较简单
好比个人payload输入以下:
获得的应该是有“Knuff测试”文字,和角标数变为999,咱们能够看下结果,与预料是一致的:
预期结果
有了这个工具也更加方便了咱们的iOS push的调试。
参考资料iOS推送之远程推送(iOS Notification Of Remote
Notification):https://www.jianshu.com/p/4b9...玩转 iOS 10 推送 —— UserNotifications
Framework(合集):https://www.jianshu.com/p/f57...用iOS10 UserNotifications框架来接收remote
notification:https://www.jianshu.com/p/b6b...iOS10推送通知进阶(Notification
Extension):https://www.jianshu.com/p/78e...
为了提升IEG苹果审核经过率,腾讯专门成立了苹果审核测试团队,打造出iOS预审工具这款产品。通过1年半的内部运营,腾讯内部应用的iOS审核经过率从平均35%提高到90%+。
现将腾讯内部产品的过审经验,以线上工具的形式共享给各位。在WeTest腾讯质量开放平台上能够在线使用。点击 http://wetest.qq.com/product/ios 便可当即体验!
若是使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:800024531
腾讯WeTest有奖征文活动进行中,欢迎投稿!
了解详情:http://wetest.qq.com/lab/view...