iOS中存在三種常見的事件通知方式:NSNofiticationCenter、KVO Notification 和 User Notifications,其中 User Notifications,就是本文將要探討的用戶通知。php
咱們都知道 iOS 系統常常的有一些與 App 相關的通知欄消息,這些消息每每伴隨着提示音以及 App 的桌面圖標右上角的未讀消息提示,這些通知就是 iOS 的用戶通知。程序員
用戶通知分爲兩類:本地通知和遠程通知,其中遠程通知又稱爲推送通知。json
二者最主要的區別是:本地通知是由 App 發送到當前設備上,不須要網絡支持;而遠程通知是由 App 的服務器發送到蘋果的 APNs 服務器,並由 APNs 服務器轉發到相應設備(由 App 服務器指定接收通知的設備)。緩存
二者最主要的共同點是:本地通知和遠程通知對用戶的表現形式是相同的,二者都可以採用通知欄消息、App 桌面圖標右上角角標和提示音的方式通知用戶。服務器
及時有效的(不管是在前臺仍是後臺)向用戶發送消息(聊天信息、新聞、待辦事項、天氣變化等)是用戶通知最大的優點。網絡
此外,有效合理的使用用戶通知,可讓咱們的 App 有更好的體驗,如:併發
本文後續內容將以應用開發者的角度對用戶通知進行深刻的探討,本文討論內容針對iOS7/8/9,有關 iOS10 系統的用戶通知會另作講解。app
本文中的遠程通知使用了 Simplepush.php ,內部代碼很簡單,可以使用該腳本自定義遠程通知的內容,ide
本文主要參考了蘋果官方的 Local and Remote Notification Programming Guide 以及本文用到的接口的官方文檔。測試
對於 iOS7,若是用戶沒有在系統設置裏關閉該 App 的通知功能,那麼開發者無需作任何操做便可使用本地通知功能。
對於 iOS8 及之後的系統,若須要使用本地通知功能,則須要註冊通知類型。
通知類型有四種:角標(UIUserNotificationTypeBadge)、提示音(UIUserNotificationTypeSound)、提示信息(UIUserNotificationTypeAlert)和無任何通知(UIUserNotificationTypeNone)。
你能夠註冊上訴四種通知類型的任意組合,但最終可用的通知形式須要根據用戶對此 App 通知的設置肯定。好比:App 內部註冊了角標、提示音和提示信息,可是用戶關閉了聲音通知,那麼收到本地通知時是不會有提示音的。
對於 iOS8 及之後的系統,註冊本地通知的代碼示例以下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 只有 iOS8 and later 才須要 if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)]) { // 這裏 types 能夠自定義,若是 types 爲 0,那麼全部的用戶通知均會靜默的接收,系統不會給用戶任何提示(固然,App 能夠本身處理並給出提示) UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); // 這裏 categories 可暫不深刻,本文後面會詳細講解。 UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; // 當應用安裝後第一次調用該方法時,系統會彈窗提示用戶是否容許接收通知 [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings]; } // Your own other codes. return YES; }
當系統彈窗提示用戶是否容許接收通知後,用戶可能會拒絕;咱們能夠在 AppDelegate 的 application:didRegisterUserNotificationSettings:
方法中用來查看註冊成功的通知類型,咱們能夠在拿到註冊結果後作自定義操做(好比失敗時彈個窗提示用戶當前沒法使用用戶通知)。
蘋果推薦在以後發送的本地通知時,要避免使用沒有註冊成功的通知類型(並非強制要求)。
- (void)application: (UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { if (notificationSettings.types & UIUserNotificationTypeBadge) { NSLog(@"Badge Nofitication type is allowed"); } if (notificationSettings.types & UIUserNotificationTypeAlert) { NSLog(@"Alert Notfication type is allowed"); } if (notificationSettings.types & UIUserNotificationTypeSound) { NSLog(@"Sound Notfication type is allowed"); } }
發送一個本地通知主要有以下步驟:
下面給出一段示例代碼:
- (void)scheduleLocalNotification { NSDate *itemDate = [NSDate date]; UILocalNotification *localNotif = [[UILocalNotification alloc] init]; if (localNotif == nil) return; localNotif.fireDate = [itemDate dateByAddingTimeInterval:10]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ after %i seconds scheduled.", nil), @"本地通知", 10]; localNotif.alertTitle = NSLocalizedString(@"Local Notification Title", nil); localNotif.soundName = UILocalNotificationDefaultSoundName; localNotif.applicationIconBadgeNumber = 1; NSDictionary *infoDict = [NSDictionary dictionaryWithObject:@"ID:10" forKey:@"LocalNotification"]; localNotif.userInfo = infoDict; [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; }
這裏分三種狀況討論如何處理本地通知:
應用處於前臺時,本地通知到達時,不會有提示音、通知欄橫幅提示,可是 App 桌面圖標的右上角角標是有數值顯示的,因此即便在前臺,咱們也應該對角標數量作處理
此時,咱們能夠在 application:didReceiveLocalNotification: 方法中獲取到本地通知,示例代碼以下:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { NSString *itemName = [notification.userInfo objectForKey:@"LocalNotification"]; [self.windowRootController displayNotification:[NSString stringWithFormat:@"%@ receive from didReceiveLocalNotificaition", itemName]]; // 這裏將角標數量減一,注意系統不會幫助咱們處理角標數量 application.applicationIconBadgeNumber -= 1; }
當應用處於後臺時,本地通知到達時,會根據本地通知設置的通知類型以及用戶設置的通知類型進行提示,例如鎖屏界面通知、通知欄通知、聲音、角標。
此時若是滑動鎖屏界面通知或點擊通知欄通知,則會切換應用到前臺,咱們可使用與應用處於前臺時相同的獲取通知的方式。
可是若是咱們點擊 App 桌面圖標,則沒法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標也不會變化,若是但願修改角標,則須要 App 進入前臺後將其修改。
若是應用沒有運行,當本地通知到達時,會根據本地通知設置的通知類型以及用戶設置的通知類型進行提示,例如鎖屏界面通知、通知欄通知、聲音、角標。
此時若是滑動鎖屏界面通知或點擊通知欄通知,則會打開應用,但這時咱們獲取通知的方式與前面有所不一樣,經過 application:didReceiveLocalNotification: 是沒法獲取通知的。
這種狀況咱們須要經過 application:didFinishLaunchingWithOptions: 中的 LaunchOptions 獲取通知,示例代碼以下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; if (localNotif) { NSString *itemName = [localNotif.userInfo objectForKey:@"LocalNotification"]; [self.windowRootController displayNotification:[NSString stringWithFormat:@"%@ receive from didFinishLaunch", itemName]]; // custom method [UIApplication sharedApplication].applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1; } // Your own other codes. return YES; }
一樣的,可是若是咱們點擊 App 桌面圖標,則沒法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標也不會變化,若是但願修改角標,則須要 App 進入前臺後將其修改。
在 iOS8 及之後系統中,咱們能夠定義一個與地理位置有關的本地通知,這樣當咱們跨過設定的地理區域時,系統會發送本地通知。
請求用戶容許使用定位服務:調用 CLLocationManager 的 =requestWhenInUseAuthorization=,
注意工程的 plist 中須要配置 NSLocationWhenInUseUsageDescription 選項,不然定位服務沒法正常啓用;示例代碼以下:
- (void)registerLocationBasedNotification { CLLocationManager *locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; // 申請定位權限 [locationManager requestWhenInUseAuthorization]; }
經過 CLLocationManagerDelegate 回調檢查用戶是否容許使用定位服務,若是容許了服務,那麼能夠發送一個位置相關的本地通知。
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { // 由於上面咱們是使用了 requestWhenInUseAuthorization,因此這裏檢查的是 kCLAuthorizationStatusAuthorizedWhenInUse if (status == kCLAuthorizationStatusAuthorizedWhenInUse) { [self scheduleLocalBasedNotification]; } }
建立一個位置相關的本地通知,並將其交由系統處理。
- (void)scheduleLocalBasedNotification { UILocalNotification *locationNotification = [[UILocalNotification alloc] init]; locationNotification.alertBody = @"到達xxx"; locationNotification.regionTriggersOnce = NO; // 表示每次跨越指定區域就會發送通知 locationNotification.region = [[CLCircularRegion alloc] initWithCenter:LOC_COORDINATE radius:LOC_RADIUS identifier:LOC_IDENTIFIER]; [[UIApplication sharedApplication] scheduleLocalNotification:locNotification]; }
與上面講過的 「處理收到的本地通知」 比較,這裏能夠在通知裏獲取到 region,而後能夠作自定義操做,其他全部操做均與 「處理收到的本地通知」 一致。
注意若是用戶沒有容許使用定位權限,則沒法收到位置相關的本地通知。
APNs 是蘋果提供的遠程通知的服務器,當 App 處於後臺或者沒有運行時,若是 App 的服務器(以後咱們稱爲 Provider)須要發送通知信息給客戶端,則須要藉助於 APNs 服務器。
使用 APNs 服務時,遠程通知的路由路徑爲: Provider –> 蘋果的 APNs 服務器 –> 手機設備 –> App。
在這個路徑中,Provider 與 APNs 服務器之間有一個 TLS 鏈接,Provider 經過這個鏈接將遠程通知推送到蘋果的 APNs 服務器;
手機設備與 APNs 服務器之間也會有一個 TLS 鏈接,全部發往手機設備的 APNs 遠程通知都是使用這一個 TLS鏈接,而後由設備區分遠程通知所屬的 App,進而通知給用戶某應用有遠程通知。
下面簡單介紹下這個流程:
設備與 APNs 創建鏈接的過程如圖:
須要明確的要點:
Provider 與 APNs 創建鏈接的過程如圖:
須要明確的要點:
Feedback 是 APNs 服務器提供的用於減小服務器壓力以及優化網絡的服務,基本的工做流程以下圖:
基本流程爲:
注意:
註冊遠程通知的示例代碼以下:
- (void)registerRemoteNotifications { // 區分是不是 iOS8 or later if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)]) { // 這裏 types 能夠自定義,若是 types 爲 0,那麼全部的用戶通知均會靜默的接收,系統不會給用戶任何提示(固然,App 能夠本身處理並給出提示) UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); // 這裏 categories 可暫不深刻,本文後面會詳細講解。 UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; // 當應用安裝後第一次調用該方法時,系統會彈窗提示用戶是否容許接收通知 [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings]; } else { UIRemoteNotificationType types = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound; [[UIApplication sharedApplication] registerForRemoteNotificationTypes:types]; } } - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings { // Register for remote notifications. [[UIApplication sharedApplication] registerForRemoteNotifications]; } // Handle register result. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { //得到 device token,這一步處理爲字符串的操做很重要 NSString *token = [[[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""]; NSLog(@"DeviceToken string, %@", token); [UIApplication sharedApplication].applicationIconBadgeNumber = 0; // 將 token 發送給 Provider } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"Error in registration for apns service. Error: %@", error); }
Provider 發送給 APNs 服務器的內容格式以下:
{ // aps key 是必需要有的 "aps" : { "alert" : { "title" : "通知的概要,對 8.2 之前的系統本選項無效" "body" : "通知的具體內容", "loc-key" : "GAME_PLAY_REQUEST_FORMAT", "loc-args" : [ "Jenna", "Frank"] }, "badge" : 3, // 角標數值 "sound" : 「chime.aiff" // 能夠自定義提示音 }, "userName" : "username", // aps key 以外能夠有自定義內容,須要符合 json 格式 "message" : ["hello", "world", "programmer"] }
有兩種方式:
在 Provider 端進行本地化
App 能夠將當前使用的語言發送給 Provider,Provider 在發送遠程通知前,檢查當前設備使用的語言,並作好本地化後發送給 APNs 服務器。App 發送當前使用的語言給 Provider 的示例代碼:
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0]; const char *langStr = [preferredLang UTF8String]; [self sendProviderCurrentLanguage:langStr]; // custom method
通常來講,將當前系統語言信息發送給 Provider 時,也會將 Token 一塊兒發送,這樣 Provider 纔可以在發送遠程通知時根據不一樣目的設備進行本地化處理。
此外,當應用啓動後,用戶可能會修改系統語言,這時,App 須要監聽 NSCurrentLocaleDidChangeNotification 通知,並在處理通知的方法中從新向 Provider 發送當前使用的語言。
在客戶端本地化
這種模式下,Provider 在發送遠程通知時,須要設置 Payload -> alert 中的本地化相關屬性,以下:
{ // aps key 是必需要有的 "aps" : { "alert" : { "title" : "通知的概要,對 8.2 之前的系統本選項無效", "loc-key" : "Remote Notification", "loc-args" : [ "hello", "world"] }, "badge" : 3, // 角標數值 "sound" : 「chime.aiff" // 能夠自定義提示音 } }
上面 loc-key 以及 loc-args 就是本地化相關的屬性,用於本地化 alert 中的 body。
當 App 收到此消息時,會根據系統當前的語言設置去相應的本地化文件中查找與 loc-key 對應的 value,若是 loc-key 對應的 value 是一個格式化的字符串,那麼能夠用 loc-args 傳遞參數。
假設本地化文件中: "Remote Notification" = "咱們程序員一般鍾愛:%@ %@" ,那麼提示信息就是: "咱們程序員鍾愛:hello world";
此外在本地化文件中咱們也能夠用 %n$@ 代替 %@ 用來表示使用 loc-args 的第幾個參數。
例如:"Remote Notification" = "咱們程序員一般鍾愛:%2$@ %1$@",那麼提示信息是:"咱們程序員鍾愛:world hello"。
一樣的,title-loc-key 和 title-loc-args 是對 alert 中的 title 作本地化的。
這裏分三種狀況討論如何處理遠程通知:
應用處於前臺時,本地通知到達時,不會有提示音、通知欄橫幅提示。可是 App 桌面圖標的右上角角標是有數值顯示的,因此即便在前臺,咱們也應該對角標數量作處理;
此時,咱們能夠在 application:didReceiveRemoteNotification:fetchCompletionHandler: 方法中獲取到遠程通知,示例代碼以下:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler { NSData *infoData = [NSJSONSerialization dataWithJSONObject:userInfo options:0 error:nil]; NSString *info = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding]; [self.windowRootController displayNotification:[NSString stringWithFormat:@"From didReceiveRemoteNotification: %@", info]]; // 這裏將角標數量減一,注意系統不會幫助咱們處理角標數量 application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber - 1; }
當應用處於後臺時,遠程通知到達時,會根據註冊通知是設置的通知類型以及用戶設置的通知類型進行提示,例如鎖屏界面通知、通知欄通知、聲音、角標。
此時若是滑動鎖屏界面通知或點擊通知欄通知,則會切換應用到前臺,咱們可使用與應用處於前臺時相同的獲取通知的方式。
可是若是咱們點擊 App 桌面圖標,則沒法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標也不會變化,若是但願修改角標,則須要 App 進入前臺後將其修改。
這裏有兩種處理方式:
與本地通知的處理方式相同,在 application:didFinishLaunchingWithOptions: 的 LaunchOptions 中獲取通知,不過內部代碼會略有不一樣,示例以下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; if (remoteNotif) { NSData *infoData = [NSJSONSerialization dataWithJSONObject:remoteNotif options:0 error:nil]; NSString *info = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding]; [self.windowRootController displayNotification:[NSString stringWithFormat:@"From didFinishLaunch: %@", info]]; [UIApplication sharedApplication].applicationIconBadgeNumber -= 1; } // Your own other codes. return YES; }
與應用處於先後臺時處理方式相同,使用 application:didReceiveRemoteNotification:fetchCompletionHandler: 方法,示例代碼見 "應用處於前臺" 時的處理。
對於遠程通知,推薦使用此種方式處理。
此外,對於遠程通知,若是咱們點擊 App 桌面圖標,則沒法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標也不會變化,若是但願修改角標,則須要 App 進入前臺後將其修改。
靜默推送是指應用在前臺或後臺狀態下,收到遠程通知時,沒有彈窗或橫幅提示,即便處於後臺也能夠處理遠程通知。具體使用流程以下:
在 Provider 發送遠程通知時,須要將遠程通知 Payload 中的 aps 內的 content-available 設置爲 1,以下:
aps { content-available: 1 alert: {...} }
應用須要實現 application:didReceiveRemoteNotification:fetchCompletionHandler: 方法接收靜默推送。
有幾點須要注意:
首先須要注意的是,可操做通知只適用於 iOS8 及之後的系統。
可操做通知其實並非一種新的通知形式,它只是在這本地通知和遠程通知的基礎上加了一些可操做的行爲而已。爲了直觀說明什麼是可操做通知,能夠參考下圖:
可操做通知爲用戶提供了在通知提示中方便執行操做的方式,在使用橫幅提示通知消息時,最多能夠有兩個操做,在使用彈窗提示通知消息是,最多能夠有四個操做。下面講解如何使用:
基本使用方法:
建立一個 UIMutableUserNotificationAction 對象,並按需求配置該對象的屬性,示例代碼:
UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init]; // 爲該操做設置一個 id acceptAction.identifier = @"accept"; // 設置該操做對應的 button 顯示的字符串 acceptAction.title = @"Accept"; // 指定是否須要應用處於運行狀態 acceptAction.activationMode = UIUserNotificationActivationModeBackground; // 表示該操做是否有害,若設置爲 YES,則對應的button會有高亮 acceptAction.destructive = NO; // 當鎖屏時收到可操做通知,該屬性表示是否必須解鎖才能執行該操做 acceptAction.authenticationRequired = YES;
建立一個 UIMutableUserNotificationCategory 對象,並將自定義的操做經過 setActions: 的方式設置給 category 對象。代碼以下:
// 這裏爲了測試,又新建了兩個 action,declineAction 和 maybeAction ,代碼可見 demo UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init]; // 設置一個 ID,用於本地通知或遠程通知時指定該通知可執行的操做group inviteCategory.identifier = @"Action"; // 爲彈窗模式設置 actions [inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault]; // 爲橫幅模式設置 actions [inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];
註冊通知類型以及可操做的actions
相似於本地通知和遠程通知,調用 registerUserNotificationSettings: 註冊通知,只是這裏的 setting 加入了咱們上面定義的 category。
NSSet *categories = [NSSet setWithObjects:inviteCategory, nil]; // 這裏 types 能夠自定義,若是 types 爲 0,那麼全部的用戶通知均會靜默的接收,系統不會給用戶任何提示(固然,App 能夠本身處理並給出提示) UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); // 這裏 categories 可暫不深刻,本文後面會詳細講解。 UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:categories]; // 當應用安裝後第一次調用該方法時,系統會彈窗提示用戶是否容許接收通知 [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
若是將可執行通知用於遠程通知,那麼須要按照遠程通知的註冊方式獲取 token,可參考遠程通知的註冊。
以前說過,可操做通知只是在本地通知和遠程通知的基礎上加了自定義的操做,因此發送可操做通知就是發送本地通知或遠程通知。
不過,若是但願咱們自定義的 action 有效,在發送本地通知或遠程通知時須要進行一些改變:
本地通知的可操做通知
爲 UILocalNotification 對象設置咱們自定義的 category。以下:
UILocalNotification *notification = [[UILocalNotification alloc] init];
// Other configurations
notification.category = @"Action";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
遠程通知的可操做通知
在遠程通知的 Payload 中設置咱們自定義的 category,以下:
{
"aps" : { "alert" : "You’re invited!", "category" : "Action" }
}
處理可操做通知與處理本地通知和遠程通知相同,惟一的不一樣點就是當用戶執行了某個操做後,
應用能夠在後臺運行 application:handleActionWithIdentifier:forRemoteNotification:completionHandler: 處理通知(例如後臺更新數據等操做),咱們能夠在這個回調裏快速的執行操做:
- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier // either forLocalNotification: (NSDictionary *) notification or forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler { if ([identifier isEqualToString: @"accept"]) { [self handleAcceptActionWithNotification:notification]; } // 執行自定義代碼完成後必須調用 completionHandler(); }
對於本地通知咱們可使用 application:handleActionWithIdentifier:forLocalNotification:completionHandler: 。
這裏舉個例子說明:
假如A向B發出了一個出席發佈會邀請,而且 App 是以遠程通知的方式接收到該信息,那麼當不使用可操做通知的時候,咱們須要作的事情主要包括:
那麼,若是咱們使用可操做通知,能夠很簡單的作到這件事情:
邀請通知處理完畢。
能夠看到,不管是從用戶角度仍是開發者角度,可操做通知都極大的方便了處理具備可操做動做的這類通知。
到這裏已經講解完成了用戶通知的內容,文章包含了蘋果給出的用戶通知的基本全部用法.