iOS Push詳述,瞭解一下?
歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~
作者:陳裕發, 騰訊系統測試工程師
商業轉載請聯絡騰訊WeTest獲得授權,非商業轉載請註明出處。
WeTest 導讀
本文主要對iOS Push的線上push、本地push及離線(遠端)push進行梳理,介紹了相關邏輯,測試時要注意的要點以及相關工具。小小的Push背後蘊藏著大大的邏輯!
Push種類
一、線上push
線上push:當用戶線上(APP在前臺)時,收到的狀態列的訊息提醒,稱為線上push。這個功能與蘋果系統無關,是我們自己的APP開發的一種功能,該push與設定中是否開啟“通知”無關。
這裡以iOS Qzone為例,當APP在前臺時,自己發的說說被點讚了,收到的線上push如下:
1.png
Qzone線上push
二、離線(遠端)push
離線push:當APP在離線(kill掉程序、切到後臺、鎖屏)時,收到的訊息提醒,稱為離線push。離線push是需要經過蘋果的APNs伺服器才可以推送到某臺裝置的某個APP上的,這是和本地push的本質區別。push與設定中是否開啟“通知”有關。
這裡最簡單的以大家常用的手機QQ為例,當APP在後臺、鎖屏或者被kiil了程序時,收到了訊息:
2.png
離線push
1、靜默push
靜默push用的場景不較少,這裡只做簡要介紹。
首先我們看看離線(遠端)push與靜默push的區別:
普通離線(遠端)push:收到推送後(有文字有聲音),點開通知,進入APP後,才執行-- (void)application:(UIApplication didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void result))handler )application
靜默push:收到推送(沒有文字沒有聲音),不用點開通知,不用開啟APP,就能執行(void)application:(UIApplication )application)userInfo didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void (^)(UIBackgroundFetchResultresult))handler,使用者完全感覺不到。
所以靜默push又被我們稱做 Background Remote Notification(後臺遠端推送)。靜默推送是在iOS7之後推出的一種推送方式。它與其他推送的區別在於允許應用收到通知後在後臺(background)狀態下執行一段程式碼,可用於從伺服器獲取內容更新。
三、本地push
本地push:本地推送和遠端推送的功能是一樣的,都是要提醒使用者去做某些事情。但是和遠端推送不同的就是本地推送是不需要裝置聯網的,而遠端推送是必需要裝置聯網的,因為只有聯網狀態下,才能和蘋果的APNs伺服器建立長連線,從而推送訊息。本地推送是由App自己設定的,並且傳送給安裝此App的這臺裝置,屬於一對一的對應關係。比較典型的應用是鬧鐘類似的場景。該push與設定中是否開啟“通知”有關。
最容易看到本地push的場景,可以直接在手機設定一個計時器,計時器時間到了就會彈出本地push:
3.png
本地push
4.png
由於本地push原理和作用相對於線上push和離線push都更為簡單明瞭,下文主要介紹線上push和離線push。
本地push實現
一、 iOS10以前本地push彈出方式
試驗過iOS10以前的本地push方法在iOS10+的系統也能使用,不過可能有些引數不生效。
1、立即展示( iOS10以前)
本地push稍微簡單,有兩種方式可以呼叫,一種是presentLocalNotificationNow方法,立即展示本地push:
5.png
2、延遲展示( iOS10以前)
另一種是用scheduleLocalNotification方法按計劃來彈本地推送:
6.png
如果使用這種方法,需要對推送的時間進行設定,舉個例子,設為5秒後:
7.png
二、設定本地push內容( iOS10以前)
8.png
其中alertBody是訊息內容鎖屏與不鎖屏時效果如下:
9.png
本地push效果
applicationIconBadgeNumber是訊息數量,我們可以看到這裡設定為66:
10.png
訊息數
三、處理本地push ( iOS10以前)
1、 App沒有啟動情況下處理本地push
這種情況下,當點選通知時,會啟動App,而在App中,開發人員可以通過實現AppDelegate中的方法:- (BOOL)application:(UIApplication)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,然後從lauchOptions中獲取App啟動的原因,若是因為本地通知,則可以App啟動時對App做對應的操作,比方說跳轉到某個畫面等等。
11.png
2、App執行在後臺及前臺
上面的2種情況的處理基本一致, 不同點只有當執行再後臺的時候,會有彈窗提示使用者另外一個App有通知,對於本地通知單的處理都是通過AppDelegate的方法:- (void)application:(UIApplication )application didReceiveLocalNotification:(UILocalNotification *)notification來處理的。
12.png
四、iOS10以後本地push彈出方式
iOS10以後,本地通知可以由使用 UNUserNotificationCenter來管理。
建立方法:
13.png
接下來需要需建立一個包含待通知內容的 UNMutableNotificationContent 物件:
14.png
在iOS上可以通過以下幾種觸發器來觸發本地push:
● UNCalendarNotificationTrigger 傳送本地通知的日期和時間。
● UNTimeIntervalNotificationTrigger 傳遞本地通知之前必須過期的時間。
● UNLocationNotificationTrigger 使用者必須達到的地理位置才能提供本地通知。
● UNPushNotificationTrigger 表示通知是從Apple推送通知服務傳送的物件。
假如以時間間隔(TimeInterval)來觸發,則設定觸發器程式碼為:
15.png
推送本地push的程式碼為:
16.png
線上、離線(遠端)push流程
一、線上push流程
線上push相對簡單,因為是內部實現,具體流程如上面所示。
1、判斷app是否線上
此處可以根據APP自身的後臺策略如上一次與後臺互動的時間等方法來判斷APP是否線上或者離線。認為線上,會發送線上push,否則,傳送離線push。
2、線上push特點
● 線上push有以下幾個特點:
● 不需要經過蘋果APNs。
● 需要自己實現長連結。
● 程式碼在app內部實現。
二、離線(遠端)push流程
17.png
離線push流程
主要流程為:
● 伺服器端將訊息先發送到蘋果的APNs
● 由蘋果的APNs將訊息推送到客戶的裝置端
● 由iOS系統將接收到的訊息傳遞給相應的App。
簡而言之離線push是蘋果系統的行為,與app狀態無關,能夠直接推送到指定手機的指定app。
在進一步瞭解離線push前,我們有必要先了解幾個名詞。
1、離線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更全面的介紹可以看官方文件:
—payload
什麼是payload?對於每一條傳送給APNs的推送訊息,都包含一個payload,通常是組成了一個JSON的Dictionary,這其中必不可少的是aps屬性,它對應的value也是一個Dictionary,包含一些但不限於以下內容:標題、副標題、內容、附件、category等,如
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會在更新系統、擦除設定重置後變化,在一定時間後會過期)
2、離線push詳細流程
知道了以上概念後我們重新來看一下離線(遠端)push的詳細流程:
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。
三、對離線(遠端)push的響應
1、iOS 7以上對離線(遠端)push時的響應
iOS 7以上關於接受離線push有兩個函式
20.png
那麼這兩個函式有什麼區別呢?其實這兩個方法都是用來處理離線push的。
差別就是,如果app在前臺是收到離線(遠端)push,那麼就會呼叫
21.png
相對的,如果在後臺或者殺程序情況下,點選收到的離線push,那麼就會呼叫,如果沒有實現
22.png
則會呼叫
23.png
若實現了前者,就只調用前者。
2、iOS 10以上對離線(遠端)push的響應
iOS10對push的處理主要增加了兩個方法
24.png
其中前者是對APP在前臺時收到push時的處理,後者是點選push進入APP執行的函式。
用得比較多的是後者,我們可以舉個例子,點選push進入APP後如何獲取push的訊息、角標、標題等內容:
25.png
iOS 10關於push的一些新特性
iOS10新增的UserNotifications框架,主要有了這樣幾方面的更新:
● 用UserNotifications框架替換了原先與通知相關的介面,通知文字可分為title、subtitle和body三部分,通知可攜帶附件
● 系統在展示通知之前,可以喚起app附帶的service extension,並且允許它改動通知的內容
● 使用者在對通知右滑檢視、下拉或者3d touch的時候,通知會展開,展開後頁面的佈局可以由app附帶的content extension來決定
一、push的多樣性
iOS10以前的push只有文字,甚至沒有標題。
iOS10以後的push更加多樣化,可以有主標題,副標題,甚至還有附件,這裡以我司的騰訊新聞為例(有標題,內容,和附件):
26.png
騰訊新聞push
3D touch點入詳情以後:
27.png
騰訊新聞push詳情
這裡我們驚奇的發現,除了可以攜帶圖片這樣的附件、push還能展開詳情以外,進入詳情以後,下面還多了“開啟”、“收藏”、“不感興趣”這些選項,這裡就涉及到以下iOS10的新特性。
二、push攜帶附件
因為payload有大小限制,所以如果remote notification想要攜帶附件,那麼payload上只能帶上如附件下載地址之類的資訊,等通知到達客戶端後由service extension下載附件到本地,然後在初始化UNNotificationAttachment物件時傳入附件在本地的URL。
28.png
初始化UNNotificationAttachment物件時,可以傳入option引數。這裡的option引數可以強制指定附件的型別,可以選擇是否展示縮圖,以及縮圖擷取自附件的哪一幀、哪一部分。
目前iOS10通知只將幾種格式的圖片、音訊和視訊作為附件,附件的大小也有一定限制,具體可以看官方文件中的限制說明。
關於附件的更加詳細的說明,可以參考官方文件:
三、攜帶action的通知
上面提到的“開啟”、“收藏”、“不感興趣”這些選項其實就是push攜帶的action,其實從iOS8開始,通知已經可以攜帶action了。而在iOS10中,通知的action被放在了更明顯的位置,與action相關的介面也有了很大變化。
決定一個通知應該有哪些action呢?在payload中,這是由category欄位決定的。如果我們希望一個通知能攜帶若干個action,我們就需要將若干個action和一個category繫結起來。通知到達前端後,系統會根據category的名字來決定要給這個通知展示哪些action:
29.png
怎麼得知使用者選了哪個action並做出相應操作呢?這需要給UNUserNotificationCenter指定一個delegate:
30.png
然後在delegate的類中實現
31.png
方法:通過response.notification.request.content.categoryIdentifier和response.actionIdentifier就可以得知使用者選擇的action了。
四、改變push內容
這裡主要講應用的比較多的離線(遠端)push的改變push方法
1、改變本地push內容
本地push,只要request的id一樣,那麼就可以更新推送:
更新的例子:
31.png
32.png
此外,還有刪除所有推送等,都在UNUserNotificationCenter.h中實現。
2、改變離線(遠端)push內容
目前遠端push只支援更新push內容,更新需要通過新的欄位apps-collapse-id來作為唯一標示。方法是在HTTP/2 請求頭中使用相同的apns-collapse-id,這樣收到同樣的apns-collapse-id的push時,push內容便會更新。
使用場景:比較容易理解的一個場景就是球賽比分,比如現在是1:0,如果變成1:1的話,只需要重新整理原來的新聞,這樣使用者就不會因為同一場比賽收到多條push。
五、兩個extension
有兩個與push相關的extension,可能我們會好奇這兩個extension有什麼不同,為什麼需要兩個?它們分別實現什麼功能呢?
33.png
push相關extension
1、notification service extension
給app新增notification service extension後,系統會在收到通知後喚醒它,並允許它修改通知的內容,之後再展示這個通知。
service extension只對remote notification起作用,local notification是無法喚起它的。
如果想要讓系統喚起service extension的話,payload必須符合這樣幾個條件:
1) 必須增加mutable-content欄位併為1,這表示允許客戶端修改這個通知:
payload(舉例)如下:
34.png
2)這個通知必須展示一個alert,如果只是一個修改badge的通知的話,是不會喚起service extension的
3)靜默推送是不能喚起service extension的,所以payload中不能有”content-available” : 1欄位
所以,通過這個notification service extension,你可以在接收到推送之後、展示推送之前處理一些事情,比如說更新一下推送內容,或者在後臺做一些其他事情。
2、notification content extension
另一項notification content extension用於完全自定義推送展開後的檢視。上面騰訊新聞的展開後的檢視就是通過這個notification content extension實現的。
依然以騰訊新聞為例子:
35.png
展開介面
這裡Notification Content Extension大展拳腳的地方,在這裡可以自定義繪製不同的內容,將希望展現給使用者的額外資訊可以載入這裡。
下半部分的notification action的實現就是在上面提到的“攜帶action的通知”。
測試要點
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 就有角標的情況存在。
相關工具
使用方法也比較簡單
37.png
比如我的payload輸入如下:
38.png
得到的應該是有“Knuff測試”文字,和角標數變為999,我們可以看下結果,與預料是一致的:
39.png
預期結果
有了這個工具也更加方便了我們的iOS push的除錯。
此文已由作者授權騰訊雲+社群釋出,更多原文請點選
搜尋關注公眾號「雲加社群」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社群!