本文旨在對 iOS 推送(以下簡稱 推送
)進行一個完整的剖析,如果你之前對推送一無所知,那麼在你認真地閱讀了全文後必將變成一個推送老手,你將會對其中的各種細節和原理有充分的理解。以下是 pikacode 使用 iOS 推送的一些經驗,歡迎互相交流,指出錯漏之處。
推送服務
可以說是所有 App 的標配,不論是哪種類型的 App,推送都從很大程度上決定了 App 的 打開率、使用率、存活率
。因此,熟知並掌握推送原理及方法,對每一個開發者來說都是必備技能,對每一個依賴 App 的公司來說都至關重要。
從 iOS 10 新增的 UserNotifications Framework
可以發現,Apple 整合了原有散亂的 API,並且增加了許多強大的功能。以 Apple 官方的角度來看,也必然是相當重視推送服務對 App 的影響、以及對 Apple 生態圈長遠發展的影響。
必須
購買 Apple 開發者賬號,並使用特定的 推送證書
使用免費帳號不能推送
那如果我們使用的是第三方推送服務(以下簡稱 第三方
)呢?比如「極光推送」。也必須購買開發者帳號。因為所有的第三方都會將推送請求發至 APNs(Apple Push Notification service 蘋果推送通知服務),所有推送均是由 APNs 下發。
如何注冊及正確的配置證書,參考這裡 iOS 證書設置指南
仍然能夠推送及接收(通知中心通知、頂部彈窗、刷新 App 右上角的小圓點即 badge
[以下簡稱 角標
] 等都會由系統來控制和展示)
收到推送時,是無法在 App 的代碼中獲取到推送內容的。因為沙盒機制,此時 App 的任何代碼都不可能被執行
在代碼中注冊推送服務:
#ifdef __IPHONE_8_0if ([[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 傳給極光推送}
綜上,注冊及接收推送 必須
使用真機,必須
連網
使用你們公司或第三方的服務端向 APNs 發送推送請求(包含 推送內容+App描述+手機描述
)
APNs 接收並驗證推送請求
APNs 利用網絡搜索並定位指定設備,下發推送
手機收到推送,系統根據 App 狀態進行處理
前台收到:
系統會將推送內容傳到 didReceiveRemoteNotification
後台收到:
如果開啟了 Remote Notification
,系統將推送傳到 didReceiveRemoteNotification:fetchCompletionHandler:
(見 Tip 5 - 後台推送)
展示彈窗、推送中心、聲音、角標
退出收到:
如果點擊推送彈窗/通知中心而啟動 App,系統將推送傳到 didFinishLaunchingWithOptions
展示彈窗、推送中心、聲音、角標
本地推送,可指定推送時間,在該時間准時彈出推送通知
遠程推送,分為 普通推送/後台推送/靜默推送
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 均是第三方提供的用於分類推送的功能。
廣播
無差別發送給所有用戶
別名 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 來下發消息
可用於對 核心用戶
、旗艦用戶
的 精准推送
消息
)和推送的區別,消息:不需要 Apple 推送證書
由第三方的服務器下發,而不是 APNs
相比推送,更快速,幾乎沒有延遲,可用於 IM 消息的即時送達
通過長連接技術下發消息,因此
手機必須啟動並與第三方服務器建立連接
如果手機啟動立刻切至後台,很可能連接沒有建立
手機必須處於前台才能收到消息
手機從後台切回前台,會自動重新建立連接,並收到離線消息
沒有任何展示(彈窗、通知中心、角標、聲音),因此可以:
自定義字段實現 UI 效果
完全在靜默情況下處理 App 內部邏輯
使用一些 App Store 審核不會通過的功能,在審核時關閉功能,上架後通過接收消息,開啟相關功能
見 Tip 5 - 標簽 tag 推送
可以通過 App 自己的服務端來統計分析用戶行為,然後將指定的 tags 發送至手機,手機接收後再為用戶打上對應的 tags
首先來看推送和消息各自的特性
推送
展示性:提醒作用
延遲性:不穩定
全局性:不論 App 處於哪種狀態均能接收
丟失性:因為各種網絡原因,可能丟失。在客戶端不能獲取歷史紀錄。
消息
靜默性:處理邏輯
即時性:穩定
前台性:只有處於前台才能收到
存留性:必然送達。在客戶端可以獲取歷史紀錄。
由於各自的特性都完全相反,因此 2 者結合使用是使得 App 性能最大化的必然選擇:
情景一:
QQ/微信 聊天。會同時下發一組 推送+消息
,如果用戶沒有啟動 QQ,雖有延遲但必然能夠先收到 推送
,在受到推送的提醒之後,用戶打開 App,此時收到了離線 消息
,即時更新 UI,與好友即時的發送/接收消息。(在收到推送後,斷網,然後啟動 App,你會發現此時手機裡並不會顯示剛剛推送的內容,因為它是依靠拉取消息來刷新頁面的,而不是不夠穩定的推送)
情景二:(期待您的補充...)