本文旨在對 iOS 推送(以下簡稱 推送
)進行一個完整的剖析,如果你之前對推送一無所知,那麼在你認真地閱讀了全文後必將變成一個推送老手,你將會對其中的各種細節和原理有充分的理解。以下是 pikacode 使用 iOS 推送的一些經驗,歡迎互相交流,指出錯漏之處。
推送服務
可以說是所有 App 的標配,不論是哪種類型的 App,推送都從很大程度上決定了 App 的 打開率、使用率、存活率
。因此,熟知並掌握推送原理及方法,對每一個開發者來說都是必備技能,對每一個依賴 App 的公司來說都至關重要。
從 iOS 10 新增的 UserNotifications Framework
可以發現,Apple 整合了原有散亂的 API,並且增加了許多強大的功能。以 Apple 官方的角度來看,也必然是相當重視推送服務對 App 的影響、以及對 Apple 生態圈長遠發展的影響。
準備篇
Tip 1:推送 必須
購買 Apple 開發者賬號,並使用特定的 推送證書
- 使用免費帳號不能推送
- 那如果我們使用的是第三方推送服務(以下簡稱
第三方
)呢?比如「極光推送」。也必須購買開發者帳號。因爲所有的第三方都會將推送請求發至 APNs(Apple Push Notification service 蘋果推送通知服務),所有推送均是由 APNs 下發。 - 如何註冊及正確的配置證書,參考這裏 iOS 證書設置指南
原理篇
Tip 2:推送本身是 iOS 系統的行爲,所以在 App 沒有運行的時候:
- 仍然能夠推送及接收(通知中心通知、頂部彈窗、刷新 App 右上角的小圓點即
badge
[以下簡稱角標
] 等都會由系統來控制和展示) - 收到推送時,是無法在 App 的代碼中獲取到推送內容的。因爲沙盒機制,此時 App 的任何代碼都不可能被執行
Tip 3:手機向 APNs 註冊推送服務
- 在代碼中註冊推送服務:
#ifdef __IPHONE_8_0 if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } else { UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound; [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes]; } #else UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound; [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes]; #endif
- 在第1次觸發這段代碼的時候,會有1個系統彈窗,詢問你是否允許該 App 要給你推送信息。當你選擇
允許
時,系統會打包App+手機唯一標識+證書
信息發送至 APNs 服務器註冊推送服務,APNs 系統會對該手機安裝的該 App 是否有推送權限進行驗證,所以必須要加入了 Apple Deveice 的手機,使用對應 App 的推送證書
才能夠成功的註冊。 - 如果註冊成功,則可以在
AppDelegate.m
的如下方法中獲取到deviceToken
,它是對該手機+該App
組合的一個唯一標識,當使用遠程推送時,只需將推送消息發給指定的deviceToken
即可使推送信息傳達給指定手機的指定 App 上。因此如果你使用第三方,就需要在這個方法裏將deviceToken
傳給第三方。(在 iOS 9 爲了更好的保護用戶隱私,會出現多次重複刪除/安裝 App 導致deviceToken
不斷變化的情況)-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [JPUSHService registerDeviceToken:deviceToken];//將 deviceToken 傳給極光推送 }
- 綜上,註冊及接收推送
必須
使用真機,必須
連網
Tip 4:推送從 服務端 --> App 代碼 的過程
- 使用你們公司或第三方的服務端向 APNs 發送推送請求(包含
推送內容+App描述+手機描述
) - APNs 接收並驗證推送請求
- APNs 利用網絡搜索並定位指定設備,下發推送
- 手機收到推送,系統根據 App 狀態進行處理
- 前臺收到:
- 系統會將推送內容傳到
didReceiveRemoteNotification
- 系統會將推送內容傳到
- 後臺收到:
- 如果開啓了
Remote Notification
,系統將推送傳到didReceiveRemoteNotification:fetchCompletionHandler:
(見 Tip 5 - 後臺推送) - 展示彈窗、推送中心、聲音、角標
- 如果開啓了
- 退出收到:
- 如果點擊推送彈窗/通知中心而啓動 App,系統將推送傳到
didFinishLaunchingWithOptions
- 展示彈窗、推送中心、聲音、角標
- 如果點擊推送彈窗/通知中心而啓動 App,系統將推送傳到
- 前臺收到:
推送內容篇
Tip 5:推送分爲 本地/遠程 2 種類型:
- 本地推送,可指定推送時間,在該時間準時彈出推送通知
- 遠程推送,分爲
普通推送/後臺推送/靜默推送
3 種類型。存在延遲問題(由於 Tip 1 第 2 點,APNs 的不穩定及高峯時段的巨量請求所致)- 普通推送
- 就是我們在手機上平時見到的推送
- 包含聲音、彈窗、角標、自定義字段
- App
- 處於前臺,
不會彈窗
,可通過didReceiveRemoteNotification
獲取推送內容(前臺彈窗的方法看這裏) - 處於後臺,
會彈窗
,無法獲取推送內容 - 處於退出,
會彈窗
,無法獲取推送內容 - 點擊圖標啓動,無法獲取推送內容
- 點擊推送彈窗啓動,在
didFinishLaunchingWithOptions
獲取推送內容
- 處於前臺,
- 推送內容類似如下:
{ "_j_msgid" = 200806057;//第三方附帶的 id,用於在後臺查詢送達情況 aps = { alert = "顯示內容"; badge = 1;//App 角標,可推送 n、+n、-n 來實現角標的固定、增加、減少 sound = default;//推送聲音,默認系統三全音,如需使用自己的聲音,需要將聲音文件拖拽&拷貝至 Xcode 工程目錄任意位置,並在推送時指定其文件名 }; key1 = value1;//自定義字段,可設置多組,用於處理內部邏輯 key2 = value2; }
- 後臺推送
- 各種顯示效果跟普通推送完全一樣
- 必須攜帶
"content-available" = 1;
- 必須攜帶
alert
、badge
、sound
中至少 1 個字段
- 僅 iOS 7 以後支持
- 必須在 Xcode 工程中
TARGETS - Capabilities - Background Modes - Remote notifications
開啓該功能,具體可參照 iOS 7 Background Remote Notification - App
- 處於前臺,可通過
didReceiveRemoteNotification
獲取推送內容 - 處於後臺,可通過
didReceiveRemoteNotification:fetchCompletionHandler:
獲取推送內容//獲取情況中與普通推送
的唯一不同點,此時 iOS 系統允許開發者在 App 處於後臺的情況下,執行一些代碼,大概提供幾分鐘的時間,可以用來偷偷的刷新 UI、切換頁面、下載更新包等等操作
- 處於退出,無法獲取推送內容
- 點擊圖標啓動,無法獲取推送內容
- 點擊推送彈窗啓動,在
didFinishLaunchingWithOptions
獲取推送內容
- 處於前臺,可通過
- 推送內容類似如下:
{ "_j_msgid" = 2090737306; aps = { alert = "顯示內容"; badge = 1; "content-available" = 1;//必帶字段 sound = default; }; key1 = value1; }
- 靜默推送
- 必須攜帶
"content-available" = 1;
,因此靜默必然
是後臺的 - 必須不攜帶
alert
、badge
、sound
- 可攜帶自定義字段
- 在用戶完全不知情的情況下被 App
didReceiveRemoteNotification
接收並處理(僅限 App 處於前臺時,其他狀態因沒有任何提示,故無法被用戶觸發並被代碼截獲。) - 推送內容類似如下:
{ "_j_msgid" = 3938587719; aps = { alert = ""; "content-available" = 1;//必帶字段 }; key1 = value1; }
- 必須攜帶
- 普通推送
別名/標籤篇
別名、標籤、Registration ID 均是第三方提供的用於分類推送的功能。
Tip 6:推送根據對象的不同可分爲:
- 廣播
- 無差別發送給所有用戶
- 別名
alias
推送- 1 個手機的 1 款 App 只能設置 1 個 alias(可修改)
- 用於指定一些基本屬性,如男/女性用戶
- 推送時可指定多個 alias 來下發同一內容
- 僅指定 alias 的用戶能夠收到推送
- 標籤
tag
推送- 可設置多個、可增加、清空
- 用於指定多樣的屬性,如
1000
+daily
+discount
可用於表示月消費超過 1k
、喜歡購買日用品
、偏好折扣商品
的用戶 - 如果要
刪除
,需要在上次設置時,將設置的 tags 保存至NSUserDefaults
,本次剔除不需要的 tag 後,再重新設置 - 推送時可指定多個 tag 來下發同一內容
- 手機如果設置了推送指定的多個 tag 中
任一個
tag,都能夠收到推送消息。如指定1000
+globe
+original
(千元級消費者、全球購
、原價),那麼設置了100
+globe
+discount
(百元級消費者、全球購
、折扣價)的用戶可以收到該推送消息。
Registration ID
推送- 在 Tip 3 的第 3 步時將
deviceToken
提供給第三方之後,其服務器會自動生成的指向該手機的唯一id
- 可在推送時指定多個 id 來下發消息
- 可用於對
核心用戶
、旗艦用戶
的精準推送
- 在 Tip 3 的第 3 步時將
自定義消息篇
Tip 7:自定義消息(以下簡稱 消息
)和推送的區別,消息:
- 不需要 Apple 推送證書
- 由第三方的服務器下發,而不是 APNs
- 相比推送,更快速,幾乎沒有延遲,可用於 IM 消息的即時送達
- 通過長連接技術下發消息,因此
- 手機必須啓動並與第三方服務器建立連接
- 如果手機啓動立刻切至後臺,很可能連接沒有建立
- 手機必須處於前臺才能收到消息
- 手機從後臺切回前臺,會自動重新建立連接,並收到離線消息
- 沒有任何展示(彈窗、通知中心、角標、聲音),因此可以:
- 自定義字段實現 UI 效果
- 完全在靜默情況下處理 App 內部邏輯
- 使用一些 App Store 審覈不會通過的功能,在審覈時關閉功能,上架後通過接收消息,開啓相關功能
組合大招篇
Tip 8:tags 的組合技巧
- 見 Tip 5 - 標籤 tag 推送
- 可以通過 App 自己的服務端來統計分析用戶行爲,然後將指定的 tags 發送至手機,手機接收後再爲用戶打上對應的 tags
Tip 9:推送+消息的組合技巧
- 首先來看推送和消息各自的特性
- 推送
- 展示性:提醒作用
- 延遲性:不穩定
- 全局性:不論 App 處於哪種狀態均能接收
- 丟失性:因爲各種網絡原因,可能丟失。在客戶端不能獲取歷史紀錄。
- 消息
- 靜默性:處理邏輯
- 即時性:穩定
- 前臺性:只有處於前臺才能收到
- 存留性:必然送達。在客戶端可以獲取歷史紀錄。
- 推送
- 由於各自的特性都完全相反,因此 2 者結合使用是使得 App 性能最大化的必然選擇:
- 情景一:
QQ/微信 聊天。會同時下發一組推送+消息
,如果用戶沒有啓動 QQ,雖有延遲但必然能夠先收到推送
,在受到推送的提醒之後,用戶打開 App,此時收到了離線消息
,即時更新 UI,與好友即時的發送/接收消息。(在收到推送後,斷網,然後啓動 App,你會發現此時手機裏並不會顯示剛剛推送的內容,因爲它是依靠拉取消息來刷新頁面的,而不是不夠穩定的推送) - 情景二:(期待您的補充...)
- 情景一:
原文鏈接:http://www.jianshu.com/p/e9c313df746f
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。