iOS Push詳述,瞭解一下?

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~html

本文由WeTest質量開放平臺團隊發表於雲+社區專欄前端

做者:陳裕發, 騰訊系統測試工程師git

商業轉載請聯繫騰訊WeTest得到受權,非商業轉載請註明出處。github

原文連接:http://wetest.qq.com/lab/view/380.html服務器

WeTest 導讀app


本文主要對iOS Push的在線push、本地push及離線(遠程)push進行梳理,介紹了相關邏輯,測試時要注意的要點以及相關工具。小小的Push背後蘊藏着大大的邏輯!框架


Push種類機器學習

1、在線pushide


在線push:當用戶在線(APP在前臺)時,收到的狀態欄的消息提醒,稱爲在線push。這個功能與蘋果系統無關,是咱們本身的APP開發的一種功能,該push與設置中是否打開「通知」無關。函數

這裏以iOS Qzone爲例,當APP在前臺時,本身發的說說被點讚了,收到的在線push以下:

img
1.png

Qzone在線push 
複製代碼

2、離線(遠程)push


離線push:當APP在離線(kill掉進程、切到後臺、鎖屏)時,收到的消息提醒,稱爲離線push。離線push是須要通過蘋果的APNs服務器才能夠推送到某臺設備的某個APP上的,這是和本地push的本質區別。push與設置中是否打開「通知」有關。

這裏最簡單的以你們經常使用的手機QQ爲例,當APP在後臺、鎖屏或者被kiil了進程時,收到了消息:

img
2.png

離線push 
複製代碼

一、靜默push

靜默push用的場景不較少,這裏只作簡要介紹。

首先咱們看看離線(遠程)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)狀態下運行一段代碼,可用於從服務器獲取內容更新。

3、本地push


本地push:本地推送和遠程推送的功能是同樣的,都是要提醒用戶去作某些事情。可是和遠程推送不一樣的就是本地推送是不須要設備聯網的,而遠程推送是必須要設備聯網的,由於只有聯網狀態下,才能和蘋果的APNs服務器創建長鏈接,從而推送消息。本地推送是由App本身設定的,而且發送給安裝此App的這臺設備,屬於一對一的對應關係。比較典型的應用是鬧鐘相似的場景。該push與設置中是否打開「通知」有關。

最容易看到本地push的場景,能夠直接在手機設置一個計時器,計時器時間到了就會彈出本地push:

img
3.png

本地push
複製代碼

img
4.png

因爲本地push原理和做用相對於在線push和離線push都更爲簡單明瞭,下文主要介紹在線push和離線push。

本地push實現

1、 iOS10之前本地push彈出方式


試驗過iOS10之前的本地push方法在iOS10+的系統也能使用,不過可能有些參數不生效。

一、當即展現( iOS10之前)

本地push稍微簡單,有兩種方式能夠調用,一種是presentLocalNotificationNow方法,當即展現本地push:

img
5.png

二、延遲展現( iOS10之前)

另外一種是用scheduleLocalNotification方法按計劃來彈本地推送:

img
6.png

若是使用這種方法,須要對推送的時間進行設置,舉個例子,設爲5秒後:

img
7.png

2、設置本地push內容( iOS10之前)


img
8.png

其中alertBody是消息內容鎖屏與不鎖屏時效果以下:

img
9.png

本地push效果
複製代碼

applicationIconBadgeNumber是消息數量,咱們能夠看到這裏設置爲66:

img
10.png

消息數
複製代碼

3、處理本地push ( iOS10之前)


一、 App沒有啓動狀況下處理本地push

這種狀況下,當點擊通知時,會啓動App,而在App中,開發人員能夠經過實現AppDelegate中的方法:- (BOOL)application:(UIApplication)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,而後從lauchOptions中獲取App啓動的緣由,如果由於本地通知,則能夠App啓動時對App作對應的操做,比方說跳轉到某個畫面等等。

img
11.png

二、App運行在後臺及前臺

上面的2種狀況的處理基本一致, 不一樣點只有當運行再後臺的時候,會有彈窗提示用戶另一個App有通知,對於本地通知單的處理都是經過AppDelegate的方法:- (void)application:(UIApplication )application didReceiveLocalNotification:(UILocalNotification *)notification來處理的。

img
12.png

4、iOS10之後本地push彈出方式


iOS10之後,本地通知能夠由使用 UNUserNotificationCenter來管理。

建立方法:

img
13.png

接下來須要需建立一個包含待通知內容的 UNMutableNotificationContent 對象:

img
14.png

在iOS上能夠經過如下幾種觸發器來觸發本地push:

● UNCalendarNotificationTrigger 傳送本地通知的日期和時間。

● UNTimeIntervalNotificationTrigger 傳遞本地通知以前必須過時的時間。

● UNLocationNotificationTrigger 用戶必須達到的地理位置才能提供本地通知。

● UNPushNotificationTrigger 表示通知是從Apple推送通知服務發送的對象。

假如以時間間隔(TimeInterval)來觸發,則設置觸發器代碼爲:

img
15.png

推送本地push的代碼爲:

img
16.png

在線、離線(遠程)push流程

1、在線push流程


在線push相對簡單,由於是內部實現,具體流程如上面所示。

一、判斷app是否在線

此處能夠根據APP自身的後臺策略如上一次與後臺交互的時間等方法來判斷APP是否在線或者離線。認爲在線,會發送在線push,不然,發送離線push。

二、在線push特色

● 在線push有如下幾個特色:

● 不須要通過蘋果APNs。

● 須要本身實現長連接。

● 代碼在app內部實現。

2、離線(遠程)push流程


img
17.png

離線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更全面的介紹能夠看官方文檔:

developer.apple.com/library/con…

—payload

什麼是payload?對於每一條發送給APNs的推送消息,都包含一個payload,一般是組成了一個JSON的Dictionary,這其中必不可少的是aps屬性,它對應的value也是一個Dictionary,包含一些但不限於如下內容:標題、副標題、內容、附件、category等,如

img
18.png

—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的詳細流程:

img
19.png

離線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。

3、對離線(遠程)push的響應


一、iOS 7以上對離線(遠程)push時的響應

iOS 7以上關於接受離線push有兩個函數

img
20.png

那麼這兩個函數有什麼區別呢?其實這兩個方法都是用來處理離線push的。

差異就是,若是app在前臺是收到離線(遠程)push,那麼就會調用

img
21.png

相對的,若是在後臺或者殺進程狀況下,點擊收到的離線push,那麼就會調用,若是沒有實現

img
22.png

則會調用

img
23.png

若實現了前者,就只調用前者。

二、iOS 10以上對離線(遠程)push的響應

iOS10對push的處理主要增長了兩個方法

img
24.png

其中前者是對APP在前臺時收到push時的處理,後者是點擊push進入APP執行的函數。

用得比較多的是後者,咱們能夠舉個例子,點擊push進入APP後如何獲取push的消息、角標、標題等內容:

img
25.png

iOS 10關於push的一些新特性

iOS10新增的UserNotifications框架,主要有了這樣幾方面的更新:

● 用UserNotifications框架替換了原先與通知相關的接口,通知文字可分爲title、subtitle和body三部分,通知可攜帶附件

● 系統在展現通知以前,能夠喚起app附帶的service extension,而且容許它改動通知的內容

● 用戶在對通知右滑查看、下拉或者3d touch的時候,通知會展開,展開後頁面的佈局能夠由app附帶的content extension來決定

1、push的多樣性


iOS10之前的push只有文字,甚至沒有標題。

iOS10之後的push更加多樣化,能夠有主標題,副標題,甚至還有附件,這裏以我司的騰訊新聞爲例(有標題,內容,和附件):

img
26.png

騰訊新聞push
複製代碼

3D touch點入詳情之後:

img
27.png

騰訊新聞push詳情
複製代碼

這裏咱們驚奇的發現,除了能夠攜帶圖片這樣的附件、push還能展開詳情之外,進入詳情之後,下面還多了「打開」、「收藏」、「不感興趣」這些選項,這裏就涉及到如下iOS10的新特性。

2、push攜帶附件


由於payload有大小限制,因此若是remote notification想要攜帶附件,那麼payload上只能帶上如附件下載地址之類的信息,等通知到達客戶端後由service extension下載附件到本地,而後在初始化UNNotificationAttachment對象時傳入附件在本地的URL。

img
28.png

初始化UNNotificationAttachment對象時,能夠傳入option參數。這裏的option參數能夠強制指定附件的類型,能夠選擇是否展現縮略圖,以及縮略圖截取自附件的哪一幀、哪一部分。

目前iOS10通知只將幾種格式的圖片、音頻和視頻做爲附件,附件的大小也有必定限制,具體能夠看官方文檔中的限制說明。

關於附件的更加詳細的說明,能夠參考官方文檔:

developer.apple.com/documentati…

3、攜帶action的通知


上面提到的「打開」、「收藏」、「不感興趣」這些選項其實就是push攜帶的action,其實從iOS8開始,通知已經能夠攜帶action了。而在iOS10中,通知的action被放在了更明顯的位置,與action相關的接口也有了很大變化。

決定一個通知應該有哪些action呢?在payload中,這是由category字段決定的。若是咱們但願一個通知能攜帶若干個action,咱們就須要將若干個action和一個category綁定起來。通知到達前端後,系統會根據category的名字來決定要給這個通知展現哪些action:

img
29.png

怎麼得知用戶選了哪一個action並作出相應操做呢?這須要給UNUserNotificationCenter指定一個delegate:

img
30.png

而後在delegate的類中實現

img
31.png

**方法:**經過response.notification.request.content.categoryIdentifier和response.actionIdentifier就能夠得知用戶選擇的action了。

4、改變push內容


這裏主要講應用的比較多的離線(遠程)push的改變push方法

一、改變本地push內容

本地push,只要request的id同樣,那麼就能夠更新推送:

更新的例子:

img
31.png

img
32.png

此外,還有刪除全部推送等,都在UNUserNotificationCenter.h中實現。

二、改變離線(遠程)push內容

目前遠程push只支持更新push內容,更新須要經過新的字段apps-collapse-id來做爲惟一標示。方法是在HTTP/2 請求頭中使用相同的apns-collapse-id,這樣收到一樣的apns-collapse-id的push時,push內容便會更新。

使用場景:比較容易理解的一個場景就是球賽比分,好比如今是1:0,若是變成1:1的話,只須要刷新原來的新聞,這樣用戶就不會由於同一場比賽收到多條push。

5、兩個extension


有兩個與push相關的extension,可能咱們會好奇這兩個extension有什麼不一樣,爲何須要兩個?它們分別實現什麼功能呢?

img
33.png

push相關extension
複製代碼

一、notification service extension

給app添加notification service extension後,系統會在收到通知後喚醒它,並容許它修改通知的內容,以後再展現這個通知。

service extension只對remote notification起做用,local notification是沒法喚起它的。

若是想要讓系統喚起service extension的話,payload必須符合這樣幾個條件:

  1. 必須增長mutable-content字段併爲1,這表示容許客戶端修改這個通知:

payload(舉例)以下:

img
34.png

2)這個通知必須展現一個alert,若是隻是一個修改badge的通知的話,是不會喚起service extension的

3)靜默推送是不能喚起service extension的,因此payload中不能有」content-available」 : 1字段

因此,經過這個notification service extension,你能夠在接收到推送以後、展現推送以前處理一些事情,好比說更新一下推送內容,或者在後臺作一些其餘事情。

二、notification content extension

另外一項notification content extension用於徹底自定義推送展開後的視圖。上面騰訊新聞的展開後的視圖就是經過這個notification content extension實現的。

依然以騰訊新聞爲例子:

img
35.png

展開界面
複製代碼

這裏Notification Content Extension大展拳腳的地方,在這裏能夠自定義繪製不一樣的內容,將但願展示給用戶的額外信息能夠加載這裏。

下半部分的notification action的實現就是在上面提到的「攜帶action的通知」。

測試要點

img
36.png

Q&A

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工具下載連接:github.com/KnuffApp/Kn…

使用方法也比較簡單

img
37.png

好比個人payload輸入以下:

img
38.png

獲得的應該是有「Knuff測試」文字,和角標數變爲999,咱們能夠看下結果,與預料是一致的:

img
39.png

預期結果
複製代碼

有了這個工具也更加方便了咱們的iOS push的調試。

參考資料iOS推送之遠程推送(iOS Notification Of Remote Notification):www.jianshu.com/p/4b947569a…玩轉 iOS 10 推送 —— UserNotifications Framework(合集):www.jianshu.com/p/f57e2045f…用iOS10 UserNotifications框架來接收remote notification:www.jianshu.com/p/b6be6310f…iOS10推送通知進階(Notification Extension):www.jianshu.com/p/78ef7bc04…

相關閱讀

系統負載能力淺析

搭建公衆號自動回覆功能

【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識

此文已由做者受權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

相關文章
相關標籤/搜索