這兩天借著排查“添加的Local Notification到點沒有提醒”的機會,對Local Notification, Remote Notification以及相關API做了一個梳理,發現“別有洞天”。
在iOS 8以前,蘋果只提供了一個API來獲取關于通知相關的東東,[[UIApplication sharedApplication] enabledRemoteNotificationTypes],
The values in the returned bit mask indicate the types of notifications currently enabled for the app. These types are first set when the app calls the registerForRemoteNotificationTypes: method to register itself with Apple Push Notification service. Thereafter, the user may modify these accepted notification types in the Notifications preference of the Settings app. This method returns those initial or modified values.
官方文檔中說,它的值取決于之前用registerForRemoteNotificationTypes注冊Remote Notification時所設定的types,同時也會根據用戶通知中心中的設定而變更,也就是說,enabledRemoteNotificationTypes同時會反映這兩種情況。但在實際測試中,發現只要registerForRemoteNotificationTypes注冊成功并生成用于發送PUSH的token,不論通知中心中app的設置如何變化(開啟通知、關閉通知、單獨開啟聲音、單獨關閉提醒等),enabledRemoteNotificationTypes的值都不會改變,嚴格等同于之前調用registerForRemoteNotificationTypes時設定的入參。這就意味著,只要注冊成功過Remote Notification一次,后續無論用戶如何調整app的通知設定,enabledRemoteNotificationTypes的返回值都是一樣的。
這個“特性”,對于Remote Notification可能還好,因為畢竟觸發PUSH是在服務端,在發送的時候,也無法得知本地客戶端的用戶設置,但對于Local Notification來說,這就是“致命”的,因為根據它開發者無法判斷用戶是否允許Local Notification,這也就是文章開頭“添加的Local Notification到點沒有提醒”的原因,因為現有的判斷是根據enabledRemoteNotificationTypes,而它又返回了全部3個type,但其實這時用戶已經關閉了app的通知。
幸好蘋果在iOS 8上解決了這個問題,引入了新的機制,把用戶授權App使用本地/遠程通知與Remote Notification成功注冊并生成token分離開來,前者用[[UIApplication sharedApplication] registerUserNotificationSettings:]來實現,后者通過[[UIApplication sharedApplication] registerForRemoteNotifications]與AppDelegate的[application:didRegisterUserNotificationSettings:]回調來達到,并引入了[[UIApplication sharedApplication] currentUserNotificationSettings],來同步返回用戶在通知中心中的設定狀態。
最后,很有意思的是,在兩個版本中,當App在后臺時,通知中心中此App的設定值都會影響到Local Notification是否以及如何被顯示,而當在App在前臺時,無論通知中心中的設置如何,Local Notification總是同樣地進行顯示。