中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

理解 iOS 本地通知和遠(yuǎn)程通知

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

本文主要講述了iOS的本地和遠(yuǎn)程通知的基本使用,以及某些不易注意的問題。

Note:文章有不少身旁同學(xué)提供了幫助,大量引用或轉(zhuǎn)載本文請聲明原文地址,多謝。

一:用戶通知簡介

用戶通知是什么

iOS中存在三種常見的事件通知方式:NSNofiticationCenter、KVO Notification 和 User Notifications,其中 User Notifications,就是本文將要探討的用戶通知。

我們都知道 iOS 系統(tǒng)經(jīng)常的有一些與 App 相關(guān)的通知欄消息,這些消息往往伴隨著提示音以及 App 的桌面圖標(biāo)右上角的未讀消息提示,這些通知就是 iOS 的用戶通知。

用戶通知的分類

用戶通知分為兩類:本地通知和遠(yuǎn)程通知,其中遠(yuǎn)程通知又稱為推送通知。

兩者最主要的區(qū)別是:本地通知是由 App 發(fā)送到當(dāng)前設(shè)備上,不需要網(wǎng)絡(luò)支持;而遠(yuǎn)程通知是由 App 的服務(wù)器發(fā)送到蘋果的 APNs 服務(wù)器,并由 APNs 服務(wù)器轉(zhuǎn)發(fā)到相應(yīng)設(shè)備(由 App 服務(wù)器指定接收通知的設(shè)備)。

兩者最主要的共同點是:本地通知和遠(yuǎn)程通知對用戶的表現(xiàn)形式是相同的,兩者均可以采用通知欄消息、App 桌面圖標(biāo)右上角角標(biāo)和提示音的方式通知用戶。

用戶通知有什么用處

及時有效的(無論是在前臺還是后臺)向用戶發(fā)送消息(聊天信息、新聞、待辦事項、天氣變化等)是用戶通知最大的優(yōu)勢。

此外,有效合理的使用用戶通知,可以讓我們的 App 有更好的體驗,如:

  • 當(dāng)待辦事項將要過期時可以及時提醒用戶;
  • 當(dāng)用戶執(zhí)行下載大文件任務(wù)時進(jìn)入后臺,當(dāng)下載完成后可以通知用戶;
  • 當(dāng)用戶環(huán)球旅行時,可以根據(jù)用戶的地理位置推送天氣變化等信息;
  • 當(dāng)用戶訂閱的某雜志或新聞主題有更新時,通知用戶;
  • ……

本文后續(xù)內(nèi)容將以應(yīng)用開發(fā)者的角度對用戶通知進(jìn)行深入的探討,本文討論內(nèi)容針對iOS7/8/9,有關(guān) iOS10 系統(tǒng)的用戶通知會另做講解。

本文中的遠(yuǎn)程通知使用了 Simplepush.php ,內(nèi)部代碼很簡單,可使用該腳本自定義遠(yuǎn)程通知的內(nèi)容,

本文主要參考了蘋果官方的 Local and Remote Notification Programming Guide 以及本文用到的接口的官方文檔。

二:本地通知的使用

開啟本地通知功能

  • 對于 iOS7,如果用戶沒有在系統(tǒng)設(shè)置里關(guān)閉該 App 的通知功能,那么開發(fā)者無需做任何操作即可使用本地通知功能。

  • 對于 iOS8 及以后的系統(tǒng),若需要使用本地通知功能,則需要注冊通知類型。

    通知類型有四種:角標(biāo)(UIUserNotificationTypeBadge)、提示音(UIUserNotificationTypeSound)、提示信息(UIUserNotificationTypeAlert)和無任何通知(UIUserNotificationTypeNone)。

    你可以注冊上訴四種通知類型的任意組合,但最終可用的通知形式需要根據(jù)用戶對此 App 通知的設(shè)置確定。比如:App 內(nèi)部注冊了角標(biāo)、提示音和提示信息,但是用戶關(guān)閉了聲音通知,那么收到本地通知時是不會有提示音的。

    對于 iOS8 及以后的系統(tǒng),注冊本地通知的代碼示例如下:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
            // 只有 iOS8 and later 才需要
            if ([[UIApplication sharedApplication]     respondsToSelector:@selector(registerForRemoteNotifications)]) {
            // 這里 types 可以自定義,如果 types 為 0,那么所有的用戶通知均會靜默的接收,系統(tǒng)不會給用戶任何提示(當(dāng)然,App 可以自己處理并給出提示)
            UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
            // 這里 categories 可暫不深入,本文后面會詳細(xì)講解。
            UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
            // 當(dāng)應(yīng)用安裝后第一次調(diào)用該方法時,系統(tǒng)會彈窗提示用戶是否允許接收通知
            [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
        }
    
        // Your own other codes.
        return YES;
    }

當(dāng)系統(tǒng)彈窗提示用戶是否允許接收通知后,用戶可能會拒絕;我們可以在 AppDelegate 的 application:didRegisterUserNotificationSettings:

方法中用來查看注冊成功的通知類型,我們可以在拿到注冊結(jié)果后做自定義操作(比如失敗時彈個窗提示用戶當(dāng)前無法使用用戶通知)。

蘋果推薦在之后發(fā)送的本地通知時,要避免使用沒有注冊成功的通知類型(并不是強(qiáng)制要求)。

- (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");
    }
}

發(fā)送本地通知

發(fā)送一個本地通知主要有如下步驟:

  1. 首先要按照上述 "開啟本地通知功能" 步驟注冊通知類型;
  2. 創(chuàng)建一個 UILocalNotification 對象;
  3. 設(shè)置 UILocalNotification 對象的 fireDate 屬性,該屬性表示什么時間點發(fā)送這條本地通知;
    同時可以設(shè)置 timeZone 屬性表示時區(qū),設(shè)置 timeZone 后,當(dāng)用戶跨越時區(qū)時,fireDate 會按照時區(qū)被調(diào)整(類似于鐘表調(diào)整);
    此外,可以使用 repeatInterval 和 repeatCalendar 來設(shè)置周期性的通知。
  4. 設(shè)置通知的提示信息:
    • 設(shè)置 alertTitle 作為通知的概要,設(shè)置 alertBody 作為通知的具體信息;注意這里強(qiáng)烈建議使用本地化的字符串,即 NSLocalizedString(@"This is alert body", nil); 。
      注意 alertTitle 屬性只適用于 iOS8.2 及以后的系統(tǒng)
    • 設(shè)置 applicationIconBadgeNumber 用于展示 App 桌面圖標(biāo)的右上角角標(biāo);
    • 設(shè)置 soundName, 我們一般設(shè)置為 UILocalNotificationDefaultSoundName;使用自定義 sound 在后面會進(jìn)一步講解;
    • 在設(shè)置提醒方式的值時,對于 iOS8 及以后的系統(tǒng),可以檢查下當(dāng)前提醒方式是否已經(jīng)注冊成功(可以用 [[UIApplication sharedApplication] currentUserNotificationSettings] 獲取注冊成功的通知類型)。
  5. 可以選擇設(shè)置 userInfo 屬性,該屬性一般可以存放業(yè)務(wù)有關(guān)的信息(如 ID 等),這樣收到通知后可以方便處理業(yè)務(wù)相關(guān)邏輯;
  6. 將上面創(chuàng)建的 UILocalnotification 放入通知隊列中:使用方法 scheduleLocalNotification: 會按照 UILocalnotification 中的 fireDate 進(jìn)行通知的發(fā)送,
    而使用 presentLocalNotificationNow: 會立即發(fā)送該本地通知。

下面給出一段示例代碼:

- (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];
}

處理收到的本地通知

這里分三種情況討論如何處理本地通知:

應(yīng)用處于前臺

應(yīng)用處于前臺時,本地通知到達(dá)時,不會有提示音、通知欄橫幅提示,但是 App 桌面圖標(biāo)的右上角角標(biāo)是有數(shù)值顯示的,所以即使在前臺,我們也應(yīng)該對角標(biāo)數(shù)量做處理

此時,我們可以在 application:didReceiveLocalNotification: 方法中獲取到本地通知,示例代碼如下:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    NSString *itemName = [notification.userInfo objectForKey:@"LocalNotification"];
    [self.windowRootController displayNotification:[NSString stringWithFormat:@"%@ receive from didReceiveLocalNotificaition", itemName]];
    // 這里將角標(biāo)數(shù)量減一,注意系統(tǒng)不會幫助我們處理角標(biāo)數(shù)量
    application.applicationIconBadgeNumber -= 1;
}

應(yīng)用處于后臺

當(dāng)應(yīng)用處于后臺時,本地通知到達(dá)時,會根據(jù)本地通知設(shè)置的通知類型以及用戶設(shè)置的通知類型進(jìn)行提示,例如鎖屏界面通知、通知欄通知、聲音、角標(biāo)。

此時如果滑動鎖屏界面通知或點擊通知欄通知,則會切換應(yīng)用到前臺,我們可以使用與應(yīng)用處于前臺時相同的獲取通知的方式。

但是如果我們點擊 App 桌面圖標(biāo),則無法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標(biāo)也不會變化,如果希望修改角標(biāo),則需要 App 進(jìn)入前臺后將其修改。

應(yīng)用沒有運行

如果應(yīng)用沒有運行,當(dāng)本地通知到達(dá)時,會根據(jù)本地通知設(shè)置的通知類型以及用戶設(shè)置的通知類型進(jìn)行提示,例如鎖屏界面通知、通知欄通知、聲音、角標(biāo)。

此時如果滑動鎖屏界面通知或點擊通知欄通知,則會打開應(yīng)用,但這時我們獲取通知的方式與前面有所不同,通過 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 桌面圖標(biāo),則無法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標(biāo)也不會變化,如果希望修改角標(biāo),則需要 App 進(jìn)入前臺后將其修改。

地理位置相關(guān)的本地通知

在 iOS8 及以后系統(tǒng)中,我們可以定義一個與地理位置有關(guān)的本地通知,這樣當(dāng)我們跨過設(shè)定的地理區(qū)域時,系統(tǒng)會發(fā)送本地通知。

注冊位置相關(guān)的本地通知

  1. 需要創(chuàng)建一個 CLLocationManager 對象,并為其設(shè)置一個 delegate;
  2. 請求用戶允許使用定位服務(wù):調(diào)用 CLLocationManager 的 =requestWhenInUseAuthorization=,

    注意工程的 plist 中需要配置 NSLocationWhenInUseUsageDescription 選項,否則定位服務(wù)無法正常啟用;示例代碼如下:

    - (void)registerLocationBasedNotification {
        CLLocationManager *locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        // 申請定位權(quán)限
        [locationManager requestWhenInUseAuthorization];
    }
  3. 通過 CLLocationManagerDelegate 回調(diào)檢查用戶是否允許使用定位服務(wù),如果允許了服務(wù),那么可以發(fā)送一個位置相關(guān)的本地通知。

    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
        // 因為上面我們是使用了 requestWhenInUseAuthorization,所以這里檢查的是 kCLAuthorizationStatusAuthorizedWhenInUse
        if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        [self scheduleLocalBasedNotification];
        }
    }
  4. 創(chuàng)建一個位置相關(guān)的本地通知,并將其交由系統(tǒng)處理。

    - (void)scheduleLocalBasedNotification {
        UILocalNotification *locationNotification = [[UILocalNotification alloc] init];
        locationNotification.alertBody = @"到達(dá)xxx";
        locationNotification.regionTriggersOnce = NO; // 表示每次跨越指定區(qū)域就會發(fā)送通知
        locationNotification.region = [[CLCircularRegion alloc] initWithCenter:LOC_COORDINATE radius:LOC_RADIUS identifier:LOC_IDENTIFIER];
    
        [[UIApplication sharedApplication] scheduleLocalNotification:locNotification];
    }

處理位置相關(guān)的本地通知

與上面講過的 “處理收到的本地通知” 比較,這里可以在通知里獲取到 region,然后可以做自定義操作,其余所有操作均與 “處理收到的本地通知” 一致。

注意如果用戶沒有允許使用定位權(quán)限,則無法收到位置相關(guān)的本地通知。

三:遠(yuǎn)程通知的使用

APNs 簡介

APNs 是蘋果提供的遠(yuǎn)程通知的服務(wù)器,當(dāng) App 處于后臺或者沒有運行時,如果 App 的服務(wù)器(之后我們稱為 Provider)需要發(fā)送通知信息給客戶端,則需要借助于 APNs 服務(wù)器。

使用 APNs 服務(wù)時,遠(yuǎn)程通知的路由路徑為: Provider –> 蘋果的 APNs 服務(wù)器 –> 手機(jī)設(shè)備 –> App。

在這個路徑中,Provider 與 APNs 服務(wù)器之間有一個 TLS 連接,Provider 通過這個連接將遠(yuǎn)程通知推送到蘋果的 APNs 服務(wù)器;

手機(jī)設(shè)備與 APNs 服務(wù)器之間也會有一個 TLS 連接,所有發(fā)往手機(jī)設(shè)備的 APNs 遠(yuǎn)程通知都是使用這一個 TLS連接,然后由設(shè)備區(qū)分遠(yuǎn)程通知所屬的 App,進(jìn)而通知給用戶某應(yīng)用有遠(yuǎn)程通知。

下面簡單介紹下這個流程:

設(shè)備 與 APNs

設(shè)備與 APNs 建立連接的過程如圖:

需要明確的要點:

  1. 此連接由系統(tǒng)建立并維持,無需開發(fā)人員管理;
  2. 上圖中的證書是蘋果設(shè)備本身的證書,與開發(fā)者賬號中申請的證書無關(guān);
  3. 每個設(shè)備與 APNs 服務(wù)器只需維持一條連接。

Provider 與 APNs

Provider 與 APNs 建立連接的過程如圖:

需要明確的要點:

  1. 此連接由 App 的 bundle ID 唯一確定;
  2. 上圖中 Provider certificate 需要通過開發(fā)者賬號申請生成,其中包含 App 的 bundle ID。

APNs 工作的流程

  1. 首先客戶端需要向 APNs 服務(wù)器注冊當(dāng)前 App,APNs 會返回一個 Token(注意這個過程要求 App 有合法的證書,有關(guān)證書這里不做詳細(xì)描述);注意不同應(yīng)用在同一設(shè)備上獲取的 Token 不同,同一應(yīng)用在不同設(shè)備上獲取的 Token也不同,所以 Token 是跟設(shè)備與 App 唯一綁定的;
  2. App 拿到 Token 后需要將其發(fā)送給 Provider;
  3. Provider 發(fā)送推送通知時,指定 Token 和通知內(nèi)容,并發(fā)送給 APNs 服務(wù)器;
  4. APNs 服務(wù)器會將通知發(fā)送給 Token 對應(yīng)的設(shè)備上;
  5. 設(shè)備收到通知后,根據(jù) APNs 發(fā)過來的通知中帶有的 bundleID 信息區(qū)分是哪個App的遠(yuǎn)程通知(這里應(yīng)該是根據(jù) Token 來獲取 bundleID)。

Feedback 機(jī)制

Feedback 是 APNs 服務(wù)器提供的用于減少服務(wù)器壓力以及優(yōu)化網(wǎng)絡(luò)的服務(wù),基本的工作流程如下圖:

  1. Provider 發(fā)送一個遠(yuǎn)程通知給 APNs 服務(wù)器,APNs 服務(wù)器會檢測目的設(shè)備是否在線,如果不在線,那么 APNs 服務(wù)器會暫存該消息;
  2. 當(dāng)目的設(shè)備上線后,APNs 會發(fā)送暫存的消息給目的設(shè)備(按照蘋果官方說法暫存消息只會暫存最后一條消息,之前的消息會被丟棄);
  3. 如果目的設(shè)備很久都沒有上線,那么 APNs 消息會把該設(shè)備加入 feedback 名單。Provider 可以定期去 APNs 拉新 feedback 名單;
  4. 當(dāng) Provider 再次給之前的設(shè)備發(fā)送遠(yuǎn)程通知時,需要檢查一下 feedback 名單,如果設(shè)備在這個名單,則不再發(fā)送給 APNs 了;
  5. 當(dāng)設(shè)備重新上線后,Provider 可以再將此設(shè)備移除 feedback 名單,當(dāng) Provider 更新 feedback list 后,就可以重新給該設(shè)備發(fā)送遠(yuǎn)程通知了。當(dāng)然,feedback list 的更新可能會有周期,如果需要及時有效的更新 feedback list,那么需要 App 打開后,及時通知 Provider;
  6. 這種機(jī)制的好處就是防止發(fā)送多余無用的遠(yuǎn)程通知消息,一方面可以減緩 APNs 服務(wù)器的壓力,另一方面也可以減少網(wǎng)絡(luò)流量;

開啟遠(yuǎn)程通知功能

注冊通知類型

  • 對于 iOS7,無需此步驟;
  • 對于 iOS8 及以后的系統(tǒng),若需要使用遠(yuǎn)程通知功能,則需要注冊通知類型。步驟與 "本地通知的使用" 中 "開啟本地通知功能" 是完全相同的,此處不再重復(fù)。

注冊遠(yuǎn)程通知

基本流程為:

  1. 注冊通知類型,上一小節(jié)已經(jīng)做了介紹;
  2. 使用 registerForRemoteNotifications 注冊遠(yuǎn)程通知(對于 iOS7 使用 registerForRemoteNotificationTypes: );
  3. 使用 application:didRegisterForRemoteNotificationsWithDeviceToken: 接收 APNs 返回的 Token,
    使用 application:didFailToRegisterForRemoteNotificationsWithError: 處理注冊錯誤;
  4. 如果上一步驟中注冊成功了,那么將得到的 Token 發(fā)送給 Provider。

注意:

  1. 目前看來,對于 iOS9,每次重新安裝應(yīng)用后得到的 Token 是不一樣的,而且每次重裝系統(tǒng)也會改變,所以 每次應(yīng)用啟動時都需要按上面的步驟注冊一次
  2. 不要將之前的 Token 緩存,當(dāng)需要將 Token 傳送到 Provider 時,一定要使用 registerForRemoteNotifications 獲取,并使用回調(diào)處理注冊結(jié)果;
    當(dāng)應(yīng)用注冊過通知,而且 Token 沒有改變時,系統(tǒng)會立即返回結(jié)果,不會去 APNs 請求。
    這里猜測系統(tǒng)幫助將 Token 緩存下來,且與應(yīng)用的狀態(tài)進(jìn)行了關(guān)聯(lián),如果應(yīng)用當(dāng)前狀態(tài)沒有改變,那么會立即將系統(tǒng)存下的 Token 返回。
    為了證明這點,可以將網(wǎng)絡(luò)關(guān)閉進(jìn)行測試,如果 App 沒有卸載,也是可以獲取到 Token 的;
  3. 一定要有開啟了 Push 功能的證書,才能正常使用遠(yuǎn)程推送。

注冊遠(yuǎn)程通知的示例代碼如下:

- (void)registerRemoteNotifications {
    // 區(qū)分是否是 iOS8 or later
    if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)]) {
        // 這里 types 可以自定義,如果 types 為 0,那么所有的用戶通知均會靜默的接收,系統(tǒng)不會給用戶任何提示(當(dāng)然,App 可以自己處理并給出提示)
        UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
        // 這里 categories 可暫不深入,本文后面會詳細(xì)講解。
        UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        // 當(dāng)應(yīng)用安裝后第一次調(diào)用該方法時,系統(tǒng)會彈窗提示用戶是否允許接收通知
        [[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 發(fā)送給 Provider
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"Error in registration for apns service. Error: %@", error);
}

發(fā)送遠(yuǎn)程通知

遠(yuǎn)程通知的內(nèi)容

Provider 發(fā)送給 APNs 服務(wù)器的內(nèi)容格式如下:

{
   // aps key 是必須要有的
   "aps" : {
      "alert" : {
         "title" : "通知的概要,對 8.2 以前的系統(tǒng)本選項無效"
         "body" : "通知的具體內(nèi)容",
         "loc-key" : "GAME_PLAY_REQUEST_FORMAT",
         "loc-args" : [ "Jenna", "Frank"]
      },
      "badge" : 3, // 角標(biāo)數(shù)值
      "sound" : “chime.aiff" // 可以自定義提示音
   },

   "userName" : "username", // aps key 之外可以有自定義內(nèi)容,需要符合 json 格式
   "message" : ["hello", "world", "programmer"]
}

遠(yuǎn)程通知的本地化處理

有兩種方式:

  • 在 Provider 端進(jìn)行本地化

    App 可以將當(dāng)前使用的語言發(fā)送給 Provider,Provider 在發(fā)送遠(yuǎn)程通知前,檢查當(dāng)前設(shè)備使用的語言,并做好本地化后發(fā)送給 APNs 服務(wù)器。App 發(fā)送當(dāng)前使用的語言給 Provider 的示例代碼:

    NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
    const char *langStr = [preferredLang UTF8String];
    [self sendProviderCurrentLanguage:langStr]; // custom method

一般來說,將當(dāng)前系統(tǒng)語言信息發(fā)送給 Provider 時,也會將 Token 一起發(fā)送,這樣 Provider 才能夠在發(fā)送遠(yuǎn)程通知時根據(jù)不同目的設(shè)備進(jìn)行本地化處理。

此外,當(dāng)應(yīng)用啟動后,用戶可能會修改系統(tǒng)語言,這時,App 需要監(jiān)聽 NSCurrentLocaleDidChangeNotification 通知,并在處理通知的方法中重新向 Provider 發(fā)送當(dāng)前使用的語言。

  • 在客戶端本地化

    這種模式下,Provider 在發(fā)送遠(yuǎn)程通知時,需要設(shè)置 Payload -> alert 中的本地化相關(guān)屬性,如下:

    {
         // aps key 是必須要有的
           "aps" : {
              "alert" : {
                 "title" : "通知的概要,對 8.2 以前的系統(tǒng)本選項無效",
                 "loc-key" : "Remote Notification",
                 "loc-args" : [ "hello", "world"]
              },
              "badge" : 3, // 角標(biāo)數(shù)值
              "sound" : “chime.aiff" // 可以自定義提示音
           }
    }

上面 loc-key 以及 loc-args 就是本地化相關(guān)的屬性,用于本地化 alert 中的 body。

當(dāng) App 收到此消息時,會根據(jù)系統(tǒng)當(dāng)前的語言設(shè)置去相應(yīng)的本地化文件中查找與 loc-key 對應(yīng)的 value,如果 loc-key 對應(yīng)的 value 是一個格式化的字符串,那么可以用 loc-args 傳遞參數(shù)。

假設(shè)本地化文件中: "Remote Notification" = "我們程序員通常鐘愛:%@ %@" ,那么提示信息就是: "我們程序員鐘愛:hello world";

此外在本地化文件中我們也可以用 %[email protected] 代替 %@ 用來表示使用 loc-args 的第幾個參數(shù)。

例如:"Remote Notification" = "我們程序員通常鐘愛:%[email protected] %[email protected]",那么提示信息是:"我們程序員鐘愛:world hello"。

同樣的,title-loc-key 和 title-loc-args 是對 alert 中的 title 做本地化的。

處理收到的遠(yuǎn)程通知

這里分三種情況討論如何處理遠(yuǎn)程通知:

應(yīng)用處于前臺

應(yīng)用處于前臺時,本地通知到達(dá)時,不會有提示音、通知欄橫幅提示。但是 App 桌面圖標(biāo)的右上角角標(biāo)是有數(shù)值顯示的,所以即使在前臺,我們也應(yīng)該對角標(biāo)數(shù)量做處理;

此時,我們可以在 application:didReceiveRemoteNotification:fetchCompletionHandler: 方法中獲取到遠(yuǎn)程通知,示例代碼如下:

- (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]];
    // 這里將角標(biāo)數(shù)量減一,注意系統(tǒng)不會幫助我們處理角標(biāo)數(shù)量
    application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber - 1;
}

應(yīng)用處于后臺

當(dāng)應(yīng)用處于后臺時,遠(yuǎn)程通知到達(dá)時,會根據(jù)注冊通知是設(shè)置的通知類型以及用戶設(shè)置的通知類型進(jìn)行提示,例如鎖屏界面通知、通知欄通知、聲音、角標(biāo)。

此時如果滑動鎖屏界面通知或點擊通知欄通知,則會切換應(yīng)用到前臺,我們可以使用與應(yīng)用處于前臺時相同的獲取通知的方式。

但是如果我們點擊 App 桌面圖標(biāo),則無法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標(biāo)也不會變化,如果希望修改角標(biāo),則需要 App 進(jìn)入前臺后將其修改。

應(yīng)用沒有運行

這里有兩種處理方式:

  • 與本地通知的處理方式相同,在 application:didFinishLaunchingWithOptions: 的 LaunchOptions 中獲取通知,不過內(nèi)部代碼會略有不同,示例如下:

    - (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;
    }
  • 與應(yīng)用處于前后臺時處理方式相同,使用 application:didReceiveRemoteNotification:fetchCompletionHandler: 方法,示例代碼見 "應(yīng)用處于前臺" 時的處理。

    對于遠(yuǎn)程通知,推薦使用此種方式處理。

    此外,對于遠(yuǎn)程通知,如果我們點擊 App 桌面圖標(biāo),則無法獲取到用戶通知,此時通知欄消息仍然會存在。此外,角標(biāo)也不會變化,如果希望修改角標(biāo),則需要 App 進(jìn)入前臺后將其修改。

遠(yuǎn)程通知-靜默推送

靜默推送是指應(yīng)用在前臺或后臺狀態(tài)下,收到遠(yuǎn)程通知時,沒有彈窗或橫幅提示,即使處于后臺也可以處理遠(yuǎn)程通知。具體使用流程如下:

  1. 打開應(yīng)用工程 Target 的 Capacities,將 Background Modes 選項打開,并且勾選 Remote Notifications;
  2. 在 Provider 發(fā)送遠(yuǎn)程通知時,需要將遠(yuǎn)程通知 Payload 中的 aps 內(nèi)的 content-available 設(shè)置為 1,如下:

    aps {  
         content-available: 1
         alert: {...}
    }
  3. 應(yīng)用需要實現(xiàn) application:didReceiveRemoteNotification:fetchCompletionHandler: 方法接收靜默推送。

有幾點需要注意:

  1. 使用靜默推送時,alert 字段不應(yīng)有任何信息,但可以設(shè)置 aps 內(nèi)的自定義字段;
  2. sound 和 badge 字段可以設(shè)置,但最好不設(shè)置,否則會有提示音;
  3. 靜默推送只有當(dāng)應(yīng)用處于前臺或后臺時才能處理,當(dāng)應(yīng)用沒有啟動時是收不到靜默推送的;
  4. 處理靜默推送時,不能做耗時操作,因為系統(tǒng)只為這種處理行為分配少量時間,如下載文件之類的操作請使用后臺下載服務(wù)。

可操作通知

首先需要注意的是,可操作通知只適用于 iOS8 及以后的系統(tǒng)。

可操作通知其實并不是一種新的通知形式,它只是在這本地通知和遠(yuǎn)程通知的基礎(chǔ)上加了一些可操作的行為而已。為了直觀說明什么是可操作通知,可以參考下圖:

可操作通知為用戶提供了在通知提示中方便執(zhí)行操作的方式,在使用橫幅提示通知消息時,最多可以有兩個操作,在使用彈窗提示通知消息是,最多可以有四個操作。下面講解如何使用:

四:定義可操作通知的行為

基本使用方法:

  1. 創(chuàng)建一個 UIMutableUserNotificationAction 對象,并按需求配置該對象的屬性,示例代碼:

    UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
    // 為該操作設(shè)置一個 id
    acceptAction.identifier = @"accept";
    // 設(shè)置該操作對應(yīng)的 button 顯示的字符串
    acceptAction.title = @"Accept";
    // 指定是否需要應(yīng)用處于運行狀態(tài)
    acceptAction.activationMode = UIUserNotificationActivationModeBackground;
    // 表示該操作是否有害,若設(shè)置為 YES,則對應(yīng)的button會有高亮
    acceptAction.destructive = NO;
    // 當(dāng)鎖屏?xí)r收到可操作通知,該屬性表示是否必須解鎖才能執(zhí)行該操作
    acceptAction.authenticationRequired = YES;
  2. 創(chuàng)建一個 UIMutableUserNotificationCategory 對象,并將自定義的操作通過 setActions: 的方式設(shè)置給 category 對象。代碼如下:

    // 這里為了測試,又新建了兩個 action,declineAction 和 maybeAction ,代碼可見 demo
    UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init];
    // 設(shè)置一個 ID,用于本地通知或遠(yuǎn)程通知時指定該通知可執(zhí)行的操作group
    inviteCategory.identifier = @"Action";
    // 為彈窗模式設(shè)置 actions
    [inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault];
    // 為橫幅模式設(shè)置 actions
    [inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];
  3. 注冊通知類型以及可操作的actions

    類似于本地通知和遠(yuǎn)程通知,調(diào)用 registerUserNotificationSettings: 注冊通知,只是這里的 setting 加入了我們上面定義的 category。

    NSSet *categories = [NSSet setWithObjects:inviteCategory, nil];
    // 這里 types 可以自定義,如果 types 為 0,那么所有的用戶通知均會靜默的接收,系統(tǒng)不會給用戶任何提示(當(dāng)然,App 可以自己處理并給出提示)
    UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
    // 這里 categories 可暫不深入,本文后面會詳細(xì)講解。
    UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
    // 當(dāng)應(yīng)用安裝后第一次調(diào)用該方法時,系統(tǒng)會彈窗提示用戶是否允許接收通知
    [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];

如果將可執(zhí)行通知用于遠(yuǎn)程通知,那么需要按照遠(yuǎn)程通知的注冊方式獲取 token,可參考遠(yuǎn)程通知的注冊。

發(fā)送可操作通知

之前說過,可操作通知只是在本地通知和遠(yuǎn)程通知的基礎(chǔ)上加了自定義的操作,所以發(fā)送可操作通知就是發(fā)送本地通知或遠(yuǎn)程通知。

不過,如果希望我們自定義的 action 有效,在發(fā)送本地通知或遠(yuǎn)程通知時需要進(jìn)行一些改變:

  • 本地通知的可操作通知

    為 UILocalNotification 對象設(shè)置我們自定義的 category。如下:

    UILocalNotification *notification = [[UILocalNotification alloc] init];

    // Other configurations

    notification.category = @"Action";

    [[UIApplication sharedApplication] scheduleLocalNotification:notification];

  • 遠(yuǎn)程通知的可操作通知

    在遠(yuǎn)程通知的 Payload 中設(shè)置我們自定義的 category,如下:

    {

    "aps" :  {
        "alert" : "You’re invited!",
        "category" : "Action"
    }

    }

處理可操作通知

處理可操作通知與處理本地通知和遠(yuǎn)程通知相同,唯一的不同點就是當(dāng)用戶執(zhí)行了某個操作后,

應(yīng)用可以在后臺運行 application:handleActionWithIdentifier:forRemoteNotification:completionHandler: 處理通知(例如后臺更新數(shù)據(jù)等操作),我們可以在這個回調(diào)里快速的執(zhí)行操作:

- (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];
    }

    // 執(zhí)行自定義代碼完成后必須調(diào)用
    completionHandler();
}

對于本地通知我們可以使用 application:handleActionWithIdentifier:forLocalNotification:completionHandler: 。

可操作通知到底有什么好處?

這里舉個例子說明:

假如A向B發(fā)出了一個出席發(fā)布會邀請,并且 App 是以遠(yuǎn)程通知的方式接收到該信息,那么當(dāng)不使用可操作通知的時候,我們需要做的事情主要包括:

  1. 用戶需要打開應(yīng)用;
  2. App 查看遠(yuǎn)程通知的內(nèi)容是一個邀請,那么 App 應(yīng)該彈窗提示用戶是否接受該邀請;
  3. 用戶選擇后,App 通過 Http(也可以使用其他通信協(xié)議) 將結(jié)果返回給服務(wù)器;
  4. 邀請通知處理完畢。

那么,如果我們使用可操作通知,可以很簡單的做到這件事情:

  1. 用戶選擇接受或拒絕邀請(用戶無需打開 App);
  2. App 通過可操作通知的回調(diào)處理用戶操作結(jié)果,將結(jié)果發(fā)送給服務(wù)器;
  3. 邀請通知處理完畢。

    可以看到,不論是從用戶角度還是開發(fā)者角度,可操作通知都極大的方便了處理具有可操作動作的這類通知。

五:總結(jié)

到這里已經(jīng)講解完成了用戶通知的內(nèi)容,文章包含了蘋果給出的用戶通知的基本所有用法.

 

來自:http://www.jianshu.com/p/ad43bc1a970a

 

標(biāo)簽: idc isp 代碼 服務(wù)器 腳本 開發(fā)者 權(quán)限 通信 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:Android類似微信圖片選擇器

下一篇:Java程序員變優(yōu)秀的10個要點