本文主要講解iOS收到遠程消息后客戶端的一些處理方法,iOS 10開始蘋果單獨集成一套框架專門處理通知,可謂非常方便。至于如何集成推送,比較簡單,這里不做探討,本文主要是讓大家了解收到遠程消息后應(yīng)用程序若要做相應(yīng)的操作以及點擊通知需要做的操作,該如何去處理。
先帶大家大致了解一下推送的流程,大致三步:
- 應(yīng)用程序在蘋果推送服務(wù)器APNS上注冊deviceToken,APNS返回devicetoken給應(yīng)用程序,之后發(fā)送給自己的后臺服務(wù)器;
- 后臺服務(wù)器將deviceToken和要發(fā)送的消息一起打包發(fā)送給APNS;
- APNS將消息發(fā)送給deviceToken中保存的指定設(shè)備中的指定APP

好了,現(xiàn)在開始講重點了。。。
首先應(yīng)用程序接收到遠程通知時可能處于三種狀態(tài):① 程序未啟動,退出狀態(tài);② 程序處于后臺,掛起狀態(tài);③ 程序處于前臺,運行狀態(tài)。
而程序啟動也可有兩種方法:①點擊通知;②點擊應(yīng)用圖標。
本文著重對點擊通知進行深入探討,至于點擊應(yīng)用圖標進入app,受制于apple,開發(fā)者并不能做什么,故忽略。
收到消息和點擊推送消息可能會調(diào)用的5個方法如下:
①- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
此方法是當應(yīng)用程序處于退出狀態(tài)后,點擊應(yīng)用圖標或者點擊通知欄啟動程序會調(diào)用,若是點擊了通知欄啟動,則推送信息userInfo會保存在launchOptions這里
if (launchOptions) {
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
self.userInfo = userInfo;
}
很多時候我們點擊通知欄啟動app需要跳轉(zhuǎn)到相應(yīng)的頁面(有時通知欄的消息類型不一樣,所需跳轉(zhuǎn)的頁面也不一樣),我們可以先將userInfo保存起來(self.userInfo = userInfo),待app初始化完之后,再在main控制器里的viewDidAppear里進行跳轉(zhuǎn),跳轉(zhuǎn)完之后再把userInfo清空,否則每次進入此控制器都會跳轉(zhuǎn),或者對它設(shè)置只執(zhí)行一次的操作。但是點擊通知欄除了會調(diào)用此方法,還會調(diào)用方法②(iOS 7.0)或者④(iOS 10.0),若在這個方法里做了操作還需進行另外的處理,請看下面。
② - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
此方法為iOS 7.0之后,若實現(xiàn)了此方法,則下面的方法③就會被覆蓋,不被調(diào)用。
收到通知,若處于前臺則會調(diào)用,若處于后臺并不會調(diào)用,若要想在后臺收到通知調(diào)用(app想在后臺收到通知之后做一些刷新UI的操作),則需后臺服務(wù)器配上content-available = 1這個字段,同時在Xcode里Capabilities中Background Modes里面勾選了Remote notifications為YES。點擊通知欄則一定會調(diào)用,若想點擊之后做一些跳轉(zhuǎn)頁面的操作,還需加一些判斷條件:
if (application.applicationState == UIApplicationStateActive) {
} else if (application.applicationState == UIApplicationStateInactive) {
// 在這里進行跳轉(zhuǎn)操作
} else if (application.applicationState == UIApplicationStateBackground) {
}
為什么要加判斷?上面講了,處于前臺和后臺收到通知會調(diào)用此方法,不加判斷處于前臺和后臺的話,就會自動跳轉(zhuǎn)咯。并且還要判斷①方法中是否有保存userInfo,若有說明是啟動app同時也會調(diào)用此方法但不需在這里執(zhí)行跳轉(zhuǎn)(因為你前面做了跳轉(zhuǎn)處理了,不然要跳兩次)。
總結(jié):①app處于前臺,收到通知會調(diào)用;②app處于后臺,收到通知,若配置了以上所講則會調(diào)用,否則不調(diào)用;③點擊通知欄,無論app處于后臺、前臺、退出都會調(diào)用
③ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo NS_DEPRECATED_IOS(3_0, 10_0, "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:] or -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:] for user visible notifications and -[UIApplicationDelegate application:didReceiveRemoteNotification:fetchCompletionHandler:] for silent remote notifications");
此方法,只有當app處于前臺收到或者點擊通知才會調(diào)用。若有實現(xiàn)方法②,則此方法作廢;若未實現(xiàn),點擊通知的處理與方法②一樣,就不過多贅述了。
④ - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
此方法為iOS 10獨享的,將本地推送和遠程推送都統(tǒng)一在這個方法里。只有當app處于前臺時收到通知(本地和遠程)會調(diào)用此方法。有點需要注意,若想在前臺時收到通知不展示出來,則需在此方法中這樣處理:
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound); // 不需展示
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert); // 需要展示
⑤ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;
此方法為iOS 10獨享的,將本地推送和遠程推送都統(tǒng)一在這個方法里。當app處于后臺時收到通知(本地和遠程)會調(diào)用此方法,對于遠程推送也需后臺服務(wù)器配置content-available = 1這個字段且Remote notifications為YES,否則處于后臺收到推送不調(diào)用,點擊通知則一定會調(diào)用,交互處理方法同②。
若為iOS 10,但并未實現(xiàn)④和⑤,則還是iOS 7 8 9之前一樣,調(diào)用相應(yīng)的方法。
總結(jié)(最低為iOS 7.0):
- app處于退出狀態(tài),所有方法都不調(diào)用,只有點擊應(yīng)用圖標才會調(diào)用方法①,點擊通知欄iOS 10 調(diào)用①和④,非iOS 10調(diào)用①和②;
- app處于后臺,若有content-available = 1和Remote notifications為YES,iOS 10 調(diào)用⑤,非iOS 10調(diào)用②,否則不調(diào)用;點擊通知欄又會調(diào)用一次,iOS 10 調(diào)用⑤,非iOS 10調(diào)用②;
- app處于前臺,iOS 10調(diào)用④,非iOS 10調(diào)用②;點擊通知欄又會調(diào)用一次,iOS 10 調(diào)用④,非iOS 10調(diào)用②;
以上為后臺遠程推送,需要后臺必須配置字段badge、alert、sound,若有靜默推送的需求,則必須加上content-available = 1,且必須去掉字段badge、alert、sound
以上是工作中總結(jié)出來的,希望能幫助到大家,如有不正確之出,歡迎指出。
另外,本人在處理推送時遇到了一個比較大的坑,在推送時,APNS會返回說一個無效的token,導(dǎo)致推送失敗,配合后臺花了大半天才解決,大致原因如下:
- 在推送時,遇到失效的TOKEN導(dǎo)致消息推送失敗。查閱很多資料,都說一旦遇到一個失效的TOKEN,同一個隊列中,從失效的TOKEN往后的消息都無法推送
- 即便通過APNS提供的方法,定時獲取失效TOKEN進行刪除,但是由于有一定的延遲,從失效TOKEN開始,往后一定時間內(nèi)推送的消息,還是無法正常的推送到用戶手機上
- 如果一旦遇到失效TOKEN,蘋果推送服務(wù)器,是否會主動斷開連接。
- 有人提供的方法是:每發(fā)送一定數(shù)量的消息,就檢查一下是否有失效TOKEN,如果有就刪除失效TOKEN,從新獲取新的連接。就像上面說的,因為有一定的延遲(延遲多長時間還不清楚),即便通過這種方式,好像也沒辦法保證大批量消息丟失啊。