《iOS面試之道》讀書筆記 - UserNotifications框架概況

UserNotifications 框架可以用來從應(yīng)用程序本地生成通知,也可以從你的服務(wù)器遠(yuǎn)程生成通知晶丘。對于本地通知(local notifications),應(yīng)用程序會(huì)創(chuàng)建通知內(nèi)容唐含,并指定觸發(fā)通知傳遞的條件浅浮,如時(shí)間或位置。對于遠(yuǎn)程通知(remote notifications)(也稱為推送通知)捷枯,使用某個(gè)服務(wù)器生成通知滚秩,Apple推送通知服務(wù)(APN)處理將這些通知傳遞到用戶的設(shè)備[1]

請求使用權(quán)限

請用戶允許顯示警報(bào)(alerts)淮捆,播放聲音或標(biāo)記應(yīng)用程序圖標(biāo)(badge)以響應(yīng)通知郁油。

本地和遠(yuǎn)程通知通過alert,聲音或badge引起用戶的注意攀痊。當(dāng)app未運(yùn)行或處于后臺時(shí)桐腌,仍然會(huì)發(fā)生這些互動(dòng)。通知讓用戶知道app具有相關(guān)信息供他們查看蚕苇。由于用戶可能會(huì)認(rèn)為基于通知??的交互具有破壞性哩掺,因此必須顯式地獲得使用通知的權(quán)限[2]

在app啟動(dòng)時(shí)請求授權(quán)

在app啟動(dòng)時(shí)發(fā)起授權(quán)請求涩笤,在代碼中獲取UNUserNotificationCenter對象嚼吞,并調(diào)用requestAuthorizationWithOptions:completionHandler:方法沙庐,指定app使用的所有交互類型肴敛。

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) 
                      completionHandler:^(BOOL granted, NSError * _Nullable error) {
                          // 基于用戶的授權(quán)狀態(tài)打開或關(guān)閉app的相關(guān)功能
                      }];

App首次發(fā)出此授權(quán)請求時(shí)图柏,系統(tǒng)會(huì)提示用戶授予或拒絕該請求并記錄用戶的響應(yīng)味赃。后續(xù)授權(quán)請求不會(huì)提示用戶夜惭。

根據(jù)授權(quán)配置支持的通知類型

在試圖發(fā)起本地通知之前蹂析,請確保app已獲得授權(quán)旗笔。即使最初獲取了授權(quán)倒脓,用戶也可以隨時(shí)取消app的授權(quán)設(shè)置罗心。用戶也可以改變允許的交互類型里伯,這可能回導(dǎo)致你改變通知的配置。

使用授權(quán)設(shè)置能夠?yàn)橛脩籼峁└玫捏w驗(yàn)渤闷。即使你在UNMutableNotificationContent對象中指定了適當(dāng)?shù)男畔⒓参停绻鸻pp沒有獲取相應(yīng)的授權(quán),系統(tǒng)也不會(huì)執(zhí)行交互飒箭。調(diào)用UNUserNotificationCentergetNotificationSettingsWithCompletionHandler:方法以獲取當(dāng)前的授權(quán)配置狼电。

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
    if (settings.authorizationStatus != UNAuthorizationStatusAuthorized) {
        return;
    }
    
    if (settings.alertSetting == UNNotificationSettingEnabled) {
        // 發(fā)送alert
        [self myScheduleAlertNotification];
    }
    else {
        // 發(fā)送聲音和badge
        [self badgeAppAndPlaySound];
    }
}];

即使app未獲取特定的授權(quán)蜒灰,你也可能希望使用相關(guān)信息配置通知。如果UNNotificationSettingsnotificationCenterSetting屬性設(shè)置為UNNotificationSettingEnabled肩碟,則通知中心仍會(huì)顯示包含alert的通知强窖。同時(shí)當(dāng)app處于前臺時(shí),通知也會(huì)傳遞到userNotificationCenter:willPresentNotification:withCompletionHandler:方法削祈。

從app發(fā)起本地通知

當(dāng)你想引起用戶的注意時(shí)翅溺,請從app創(chuàng)建并安排通知。

系統(tǒng)會(huì)根據(jù)你指定的時(shí)間或地點(diǎn)傳遞和處理通知岩瘦。如果在app未運(yùn)行或在后臺運(yùn)行通知時(shí)未巫,系統(tǒng)會(huì)與用戶進(jìn)行交互。如果app位于前臺启昧,系統(tǒng)會(huì)將通知發(fā)送到app以進(jìn)行處理[3]叙凡。

創(chuàng)建通知的內(nèi)容(Notification Content)

使用UNMutableNotificationContent對象表示通知內(nèi)容。

UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Weekly Staff Meeting";
content.body = @"Every Tuesday at 2pm";

指定觸發(fā)條件(Notification Trigger)

為通知指定觸發(fā)條件密末,使用UNCalendarNotificationTrigger握爷,UNTimeIntervalNotificationTriggerUNLocationNotificationTrigger對象。不同的 trigger 需要使用不同的參數(shù)严里,例如基于日歷的 trigger 需要指定觸發(fā)日期和時(shí)間新啼。
下面的代碼展示了如何配置每周二下午2點(diǎn)發(fā)送的通知。NSDateComponents對象指定事件的觸發(fā)時(shí)間刹碾。配置 trigger 的repeats屬性會(huì)使系統(tǒng)在傳遞事件后重新規(guī)劃該事件燥撞,從而實(shí)現(xiàn)周期性觸發(fā)通知的效果。

NSDateComponents *components = [[NSDateComponents alloc] init];
components.calendar = [NSCalendar currentCalendar];
components.weekday = 3;
components.hour = 14;

UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];

創(chuàng)建并注冊通知請求(Notification Request)

創(chuàng)建一個(gè)包含內(nèi)容(content)和觸發(fā)條件(trigger)的UNNotificationRequest對象迷帜,并調(diào)用addNotificationRequest:withCompletionHandler:方法使系統(tǒng)規(guī)劃你的請求(request)物舒。

// Create the request
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[NSUUID UUID] content:content trigger:trigger];

// Schedule the request with the system
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    if (error) {
        // Handle any errors
    }
}];

取消已規(guī)劃的請求

一旦被規(guī)劃,通知請求(Notification Request)將保持活動(dòng)狀態(tài)戏锹,直到滿足其觸發(fā)條件冠胯,或被顯式地取消。一般來說锦针,你可以在條件改變時(shí)取消通知荠察,而不再需要通知用戶。取消一個(gè)活動(dòng)的通知請求奈搜,需要調(diào)用UNUserNotificationCenterremovePendingNotificationRequestsWithIdentifiers:removeAllPendingNotificationRequests方法悉盆。

向APNs注冊你的app

與 APNs 通信并接收標(biāo)識 app 的唯一設(shè)備令牌(unique device token)

APNs 必須先知道用戶設(shè)備的地址,然后才能向該設(shè)備發(fā)送通知馋吗。此地址采用設(shè)備令牌(device token)的形式焕盟,該設(shè)備令牌對于設(shè)備和應(yīng)用程序都是唯一的。在啟動(dòng)時(shí)耗美,app 與 APNs 通信并接收其t oken京髓,然后將其轉(zhuǎn)發(fā)到你的業(yè)務(wù)服務(wù)器。你的服務(wù)器包含該 token 及其發(fā)送的任何通知商架。

注意堰怨,即使兩個(gè) app 都安裝在同一設(shè)備上,一個(gè) app 的 token 也無法用于其他 app蛇摸。兩個(gè) app 都必須請求自己唯一的 token 并將其轉(zhuǎn)發(fā)到業(yè)務(wù)服務(wù)器[4]备图。

啟用推動(dòng)通知功能

要推送通知功能,app 必須具有合適的 entitlement赶袄。要將這些 entitlements 添加到 app揽涮,需要在Xcode項(xiàng)目中啟用推送通知功能,如下圖所示饿肺。在 iOS 中啟用此選項(xiàng)會(huì)將 aps-environment 代碼簽名 entitlement 添加到 app 中蒋困。

啟用推送通知功能

注意,在你的開發(fā)者帳戶中敬辣,你還必須啟用該項(xiàng)目的App ID推送通知服務(wù)雪标。有關(guān)配置開發(fā)者帳戶的詳細(xì)信息,請轉(zhuǎn)到開發(fā)者帳戶頁面溉跃。

注冊你的app并獲取應(yīng)用程序的設(shè)備令牌(Device Token)

使用 APNs 注冊你的 app 并接收全局唯一設(shè)備令牌(Globally Unique Device Token)村刨,該 token 是當(dāng)前設(shè)備上你 app 的有效地址。業(yè)務(wù)服務(wù)器必須先具有此 token 才能向設(shè)備發(fā)送通知撰茎。
每次使用蘋果提供的API(Apple-provided APIs)啟動(dòng) app 時(shí)嵌牺,都需注冊 app 并接收 token。注冊過程在各個(gè)平臺上類似:

  • 在iOS和tvOS中龄糊,調(diào)用UIApplication對象的registerForRemoteNotifications方法用來請求 token逆粹。注冊成功后,你將在application:didRegisterForRemoteNotificationsWithDeviceToken:方法中收到 token绎签。
  • 在 macOS 中枯饿,調(diào)用NSApplication對象的registerForRemoteNotificationTypes:方法用來請求 token。注冊成功后诡必,你將在application:didRegisterForRemoteNotificationsWithDeviceToken:方法中收到 token奢方。
  • 在 watchOS 中,不需要顯式地注冊遠(yuǎn)程通知爸舒。用戶的 iPhone 會(huì)在適當(dāng)?shù)臅r(shí)間自動(dòng)將遠(yuǎn)程通知轉(zhuǎn)發(fā)給watchOS應(yīng)用程序蟋字。

除了處理成功注冊 APNs 之外,還要實(shí)現(xiàn)application:didFailToRegisterForRemoteNotificationsWithError:方法扭勉,處理失敗的注冊鹊奖。如果用戶的設(shè)備未連接到網(wǎng)絡(luò),APNs 服務(wù)器因任何原因無法訪問涂炎,或者應(yīng)用程序沒有正確的代碼簽名權(quán)限忠聚,則注冊可能會(huì)失敗设哗。發(fā)生故障時(shí),設(shè)置一個(gè)標(biāo)志并嘗試稍后再次注冊两蟀。

下面代碼實(shí)現(xiàn)了遠(yuǎn)程通知和接收相應(yīng) token 所需的委托方法网梢。sendDeviceTokenToServer方法是 app 用于將數(shù)據(jù)發(fā)送到其提供程序服務(wù)器的自定義方法。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch
    
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    return YES;
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [self sendDeviceTokenToServer:deviceToken];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    // Try again later
}

注意赂毯,切勿在本地存儲中緩存 token战虏。當(dāng)用戶從備份還原設(shè)備,用戶在新設(shè)備上安裝 app 以及用戶重新安裝操作系統(tǒng)時(shí)党涕,APNs 會(huì)發(fā)出新 token烦感。如果你要求系統(tǒng)每次都提供令牌,則可以保證獲得最新的令牌膛堤。

將token轉(zhuǎn)發(fā)到業(yè)務(wù)服務(wù)器

獲取 token 后手趣,打開從應(yīng)用程序到提供商服務(wù)器的網(wǎng)絡(luò)連接。安全地轉(zhuǎn)發(fā) token 以及識別服務(wù)器特定用戶所需的任何其他信息骑祟。例如回懦,可以包含用戶的登錄名或?qū)⑵溥B接到業(yè)務(wù)服務(wù)的內(nèi)容。加密任何通過網(wǎng)絡(luò)發(fā)送的信息次企。
在業(yè)務(wù)服務(wù)器上怯晕,將令牌存儲在安全的位置,您可以在其中訪問它們以發(fā)送通知缸棵。生成通知時(shí)舟茶,你的服務(wù)器必須能夠向特定設(shè)備發(fā)送通知。因此堵第,如果通知鏈接到用戶的帳戶吧凉,請使用用戶的帳戶信息存儲設(shè)備令牌。由于用戶可以擁有多個(gè)設(shè)備踏志,因此需要處理多個(gè)設(shè)備令牌阀捅。

如需獲取如何將 payload 和 token 發(fā)送到 APNs 的信息,參考向APNs發(fā)送通知請求针余。

聲明可操作的通知類型

區(qū)分通知并將操作按鈕添加到通知界面

可操作的通知(Actionable notifications)允許用戶響應(yīng)已發(fā)送的通知饲鄙,而無需啟動(dòng)相應(yīng)的app。其他通知在通知界面中顯示信息圆雁,但用戶唯一的操作方法是啟動(dòng)app忍级。對于可操作的通知,除通知界面外伪朽,系統(tǒng)還顯示一個(gè)或多個(gè)按鈕轴咱。點(diǎn)擊按鈕會(huì)將所選操作發(fā)送到app,然后app在后臺處理操作[5]

聲明自定義操作(Custom Action)和通知類型(Notification Type)

必須在 app 啟動(dòng)時(shí)聲明支持的所有操作(action)朴肺。你可以聯(lián)合使用類別動(dòng)作對象來聲明一個(gè)操作:使用UNNotificationCategory對象定義 app 支持的通知類型窖剑,使用UNNotificationAction對象定義要為每種類型顯示的按鈕。例如戈稿,一個(gè)會(huì)議邀請的通知可能包括接受或拒絕邀請的按鈕苛吱。

每一個(gè)UNNotificationCategory對象都有一個(gè)唯一標(biāo)識和用于處理該類型通知的選項(xiàng)。identifier屬性中的字符串是類別對象中最重要的部分器瘪,生成通知時(shí),必須在通知的有效載荷(payload)中包含相同的字符串绘雁。系統(tǒng)使用該字符串來定位相應(yīng)的類別對象和任何操作橡疼。

要將操作與通知類別相關(guān)聯(lián),請為其分配一個(gè)或多個(gè)UNNotificationAction對象庐舟。每個(gè)操作對象都包含要顯示給用戶的本地化字符串以及表示你希望如何處理該操作的選項(xiàng)欣除。例如,當(dāng)您將操作標(biāo)記為破壞性(destructive)時(shí)挪略,系統(tǒng)會(huì)以不同的高亮形式顯示該操作以指示其行為历帚。

下面的代碼展示了如何使用兩個(gè)操作注冊自定義類別。除標(biāo)題和選項(xiàng)外杠娱,每個(gè)操作都有唯一的標(biāo)識符挽牢。當(dāng)用戶選擇操作時(shí),系統(tǒng)會(huì)將該標(biāo)識符傳遞給app摊求。

UNNotificationAction *acceptAction = [UNNotificationAction actionWithIdentifier:@"ACCEPT_ACTION" 
                                                                          title:@"Accept" 
                                                                        options:UNNotificationActionOptionNone];
UNNotificationAction *declineAction = [UNNotificationAction actionWithIdentifier:@"DECLINE_ACTION" 
                                                                           title:@"Decline" 
                                                                         options:UNNotificationActionOptionNone];

// Define the notification type
UNNotificationCategory *meetingInviteCategory = [UNNotificationCategory categoryWithIdentifier:@"MEETING_INVITATION" 
                                                                                       actions:@[acceptAction, declineAction] 
                                                                             intentIdentifiers:@[] 
                                                                 hiddenPreviewsBodyPlaceholder:@"" 
                                                                                       options:UNNotificationCategoryOptionCustomDismissAction];

// Register the notification type
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObject:meetingInviteCategory]];

注意:所有操作(action)對象都必須具有唯一標(biāo)識符禽拔。處理操作時(shí),標(biāo)識符是區(qū)分一個(gè)操作與另一個(gè)操作的唯一方法室叉,即使這些操作屬于不同的類別睹栖。

大多數(shù)操作只會(huì)讓用戶選擇,但文本輸入操作也允許用戶鍵入基于自定義文本的響應(yīng)(response)茧痕。然后野来,app可以將用戶輸入的響應(yīng)合并到您對操作的處理中。例如踪旷,一個(gè)聊天app可以將鍵入的文本作為對傳入消息的響應(yīng)曼氛。要?jiǎng)?chuàng)建文本輸入操作,需要?jiǎng)?chuàng)建UNTextInputNotificationAction對象而不是UNNotificationAction對象埃脏。當(dāng)用戶點(diǎn)擊輸入按鈕時(shí)搪锣,系統(tǒng)顯示可編輯的文本字段(test field)。當(dāng)系統(tǒng)向app報(bào)告該操作時(shí)彩掐,系統(tǒng)會(huì)包含用戶在響應(yīng)中鍵入的文本构舟。

在有效載荷(Payload)中包含通知類別(Notification Category)

系統(tǒng)僅顯示那些payload包含有效類別標(biāo)識符字符串(Category Identifier)的通知的操作。系統(tǒng)使用類別標(biāo)識符來查找應(yīng)用程序的已注冊類別及其相關(guān)操作。然后狗超,它使用該信息將操作按鈕添加到通知界面弹澎。系統(tǒng)使用類別標(biāo)識符來查找應(yīng)用程序的已注冊類別及其相關(guān)操作。然后努咐,它使用該信息將操作按鈕添加到通知界面苦蒿。

下面的代碼展示了如何創(chuàng)建一條會(huì)議邀請本地通知的內(nèi)容(content)。要將類別分配給本地通知渗稍,需要將相應(yīng)的字符串賦值給UNMutableNotificationContent對象的categoryIdentifier屬性佩迟。除了基本信息之外,此代碼還會(huì)將自定義數(shù)據(jù)添加到通知的userInfo字典中竿屹,稍后將使用該字典來處理該會(huì)議邀請报强。

UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Weekly Staff Meeting";
content.body = @"Every Tuesday at 2pm";
content.userInfo = @{
    @"MEETING_ID": meetingID,
    @"USER_ID": userID
};
content.categoryIdentifier = @"MEETING_INVITATION";

如果是在遠(yuǎn)程通知中添加類別標(biāo)識符,需要將category字段添加到 JSON payload的aps字典中拱燃。

{
   "aps" : {
      "category" : "MEETING_INVITATION"
      "alert" : {
         "title" : "Weekly Staff Meeting"
         "body" : "Every Tuesday at 2pm"
      },
   },
   "MEETING_ID" : "123456789",
   "USER_ID" : "ABCD1234"
}

處理選定的操作

App必須處理它定義的所有操作秉溉。當(dāng)用戶選擇一個(gè)操作時(shí),系統(tǒng)會(huì)在后臺啟動(dòng) app 并通知共享的UNUserNotificationCenter對象實(shí)例碗誉,該對象會(huì)通知其代理對象(delegate)召嘶。使用代理對象的userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:方法來識別所選操作并提供適當(dāng)?shù)捻憫?yīng)。

下面的代碼實(shí)現(xiàn)了管理會(huì)議邀請的 app 的委托方法哮缺。該方法使用響應(yīng)的actionIdentifier屬性來確定是接受還是拒絕給定的邀請弄跌。它還依賴于通知有效載荷中的自定義數(shù)據(jù)來成功處理通知。完成處理操作后尝苇,應(yīng)當(dāng)始終調(diào)用完成處理程序(completion handler)碟绑。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center 
didReceiveNotificationResponse:(UNNotificationResponse *)response 
         withCompletionHandler:(void(^)(void))completionHandler {

    // Get the meeting ID from the original notification
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    NSString *meetingID = userInfo[@"MEETING_ID"];
    NSString *userID = userInfo[@"USER_ID"];

    // Perform the task associated with the action
    if ([response.actionIdentifier isEqualToString:@"ACCEPT_ACTION"]) {
        [sharedMeetingManager acceptMeeting:meetingID forUser:userID];
    }
    else if ([response.actionIdentifier isEqualToString:@"DECLINE_ACTION"]) {
        [sharedMeetingManager declineMeeting:meetingID forUser:userID];
    }
    else {
        // Handle other actions…
    }

    // Always call the completion handler when done
    completionHandler();
}

處理通知和與通知相關(guān)的操作

響應(yīng)用戶與系統(tǒng)通知界面的交互,包括處理應(yīng)用程序的自定義操作

通知(Notifications)是將信息呈現(xiàn)在用戶面前的一種主要方式茎匠,同時(shí) app 本身也可以響應(yīng)這些通知格仲。例如 app 可以響應(yīng)這些操作[6]

  • 用戶從通知界面中選擇的操作
  • App 在前臺運(yùn)行時(shí)收到的通知
  • 靜默通知
  • PushKit相關(guān)的通知

處理 app 在前臺運(yùn)行時(shí)的通知

如果 app 在前臺運(yùn)行時(shí)收到通知,系統(tǒng)會(huì)將該通知直接發(fā)送到 app诵冒。收到通知后凯肋,可以使用通知的有效負(fù)載(Payload)執(zhí)行任何操作。例如汽馋,你可以更新 app 的界面以反映通知中包含的新信息侮东。然后,你可以禁止或修改任何已規(guī)劃的 alert豹芯。
當(dāng)通知到達(dá)時(shí)悄雅,系統(tǒng)調(diào)用UNUserNotificationCenter委托對象(delegate)userNotificationCenter:willPresentNotification:withCompletionHandler:方法。使用該方法處理通知铁蹈,并讓系統(tǒng)知道你希望如何繼續(xù)宽闲。
下面的代碼實(shí)現(xiàn)了該方法。當(dāng)會(huì)議邀請到達(dá)時(shí),app 會(huì)調(diào)用其自定義的queueMeetingForDelivery方法在 app 界面中顯示新邀請容诬。App 還要求系統(tǒng)通過將值UNNotificationPresentationOptionSound傳遞給完成處理程(completion handler)來播放通知的聲音娩梨。對于其他通知類型,該方法使通知靜音览徒。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center 
       willPresentNotification:(UNNotification *)notification 
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
    
    if ([notification.request.content.categoryIdentifier isEqualToString:@"MEETING_INVITATION"]) {
        // Retrieve the meeting details
        NSString *meetingID = userInfo[@"MEETING_ID"];
        NSString *userID = userInfo[@"USER_ID"];

        // Add the meeting to the queue
        [sharedMeetingManager queueMeetingForDelivery:meetingID forUser:userID];

        // Play a sound to let the user know about the invitation
        completionHandler(UNNotificationPresentationOptionSound);
        return;
    }
    else {
        // Handle other notification types...
    }

    // Don't alert the user for other types
    completionHandler(UNNotificationPresentationOptionNone);
}

如果你使用 PushKit 注冊了應(yīng)用狈定,則針對 PushKit 類型的通知始終會(huì)直接發(fā)送到您的應(yīng)用,并且永遠(yuǎn)不會(huì)向用戶顯示习蓬。如果 app 位于前臺或后臺纽什,系統(tǒng)會(huì)為 app 提供處理通知的時(shí)間。如果 app 未運(yùn)行躲叼,系統(tǒng)會(huì)在后臺啟動(dòng) app稿湿,以便它可以處理通知。要發(fā)送 PushKit 通知押赊,你的服務(wù)器必須將通知的主題設(shè)置為適當(dāng)?shù)哪繕?biāo),例如您的應(yīng)用程序的復(fù)雜性包斑。有關(guān)注冊PushKit通知的更多信息流礁,請參閱PushKit

修改通知中的內(nèi)容

在用戶的iOS設(shè)備上顯示之前修改遠(yuǎn)程通知的有效負(fù)載(Payload)

如果需要罗丰,你可能要在用戶的iOS設(shè)備上修改遠(yuǎn)程通知的內(nèi)容:

  • 解密以加密格式發(fā)送的數(shù)據(jù)神帅。
  • 下載大小超過最大有效載荷大小的圖像或其他媒體附件。
  • 更新通知的內(nèi)容萌抵,可能是通過合并用戶設(shè)備的數(shù)據(jù)找御。

修改遠(yuǎn)程通知需要通知服務(wù)應(yīng)用程序擴(kuò)展(service app extension),您可以將其包含在iOS應(yīng)用程序包(bundle)中绍填。應(yīng)用程序擴(kuò)展在向用戶顯示之前會(huì)接收遠(yuǎn)程通知的內(nèi)容霎桅,從而在用戶收到alert之前更新通知的有效負(fù)載(Payload)。你可以控制擴(kuò)展程序處理哪些通知讨永。

注意滔驶,通知服務(wù)應(yīng)用程序擴(kuò)展僅對配置為向用戶顯示alert的遠(yuǎn)程通知進(jìn)行操作。如果您的應(yīng)用程序禁用了alert卿闹,或者有效負(fù)載僅指定播放聲音或圖標(biāo)的標(biāo)記揭糕,則擴(kuò)展程序不生效[7]

為項(xiàng)目添加服務(wù)應(yīng)用程序擴(kuò)展(Service App Extension)

通知服務(wù)應(yīng)用擴(kuò)展程序在你的iOS應(yīng)用中作為單獨(dú)的捆綁包發(fā)布锻霎。要將此擴(kuò)展程序添加到您的應(yīng)用中:

  1. 在Xcode中選擇 File > New > Target著角。
  2. 從 iOS > Application section 中選擇 Notification Service Extension。
  3. 點(diǎn)擊 Next旋恼。
  4. 指定應(yīng)用擴(kuò)展程序的名稱和其他配置詳細(xì)信息吏口。
  5. 點(diǎn)擊 Finish。

實(shí)現(xiàn)擴(kuò)展的方法

Xcode提供的通知服務(wù)擴(kuò)展模板包含了一個(gè)可供修改的默認(rèn)實(shí)現(xiàn):

  • didReceiveNotificationRequest:withContentHandler:方法可以使用更新的內(nèi)容創(chuàng)建UNMutableNotificationContent對象。
  • 使用serviceExtensionTimeWillExpire方法終止仍在運(yùn)行的任何有效載荷修改任務(wù)锨侯。

didReceiveNotificationRequest:withContentHandler:方法大概只有30秒的時(shí)間來修改有效負(fù)載并調(diào)用提供的完成處理程序(completion handler)嫩海。如果你的代碼需要更長的時(shí)間,系統(tǒng)將調(diào)用serviceExtensionTimeWillExpire方法囚痴,此時(shí)你必須立即向系統(tǒng)返回任何內(nèi)容叁怪。如果你無法從任一方法調(diào)用完成處理程序,系統(tǒng)將顯示通知的原始內(nèi)容深滚。

下面的代碼實(shí)現(xiàn)了UNNotificationServiceExtension對象奕谭,該對象解密使用遠(yuǎn)程通知傳遞的加密消息的內(nèi)容。didReceiveNotificationRequest:withContentHandler:方法解密數(shù)據(jù)并在通知內(nèi)容成功時(shí)返回修改后的內(nèi)容痴荐。如果不成功血柳,或者時(shí)間到期,則擴(kuò)展程序返回指示數(shù)據(jù)仍處于加密狀態(tài)的內(nèi)容生兆。

// Storage for the completion handler and content
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end

@implementation NotificationService
// Modify the payload contents
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request 
                   withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

    // Try to decode the encrypted message data
    NSData *encryptedData = bestAttemptContent.userInfo[@"ENCRYPTED_DATA"] ?: [NSData data];
    NSString *decryptedMessage = [self decript:encryptedData];
    bestAttemptContent.body = decryptedMessage ?: @"(Encrypted)";

    // Always call the completion handler when done
    contentHandler(bestAttemptContent)
}

// Return something before time expires
- (void)serviceExtensionTimeWillExpire {
    // Mark the message as still encrypted
    bestAttemptContent.subtitle = @"(Encrypted)";
    bestAttemptContent.body = @"";
    contentHandler(bestAttemptContent);
}
@end

配置遠(yuǎn)程通知的有效負(fù)載

僅當(dāng)遠(yuǎn)程通知的有效負(fù)載包含以下信息時(shí)难捌,系統(tǒng)才會(huì)執(zhí)行通知內(nèi)容應(yīng)用程序擴(kuò)展:

  • 有效負(fù)載必須包含mutable-content值為1
  • 有效負(fù)載必須包含alert字典鸦难,包含title根吁,subtitlebody

下面的JSON數(shù)據(jù)展示了包含加密數(shù)據(jù)的通知 payload合蔽。設(shè)置mutable-content值為1击敌,以便用戶的設(shè)備知道運(yùn)行相應(yīng)的服務(wù)應(yīng)用程序擴(kuò)展。

{
   "aps" : {
      "category" : "SECRET",
      "mutable-content" : 1,
    "alert" : {
         "title" : "Secret Message!",
         "body"  : "(Encrypted)"
     },
   },
   "ENCRYPTED_DATA" : "Salted__·??Qê$UDì_?ù∞è   Ω^?%gq∞N?òQùw"
}

  1. UserNotifications ?

  2. Asking Permission to Use Notifications ?

  3. Scheduling a Notification Locally from Your App ?

  4. Registering Your App with APNs ?

  5. Declaring Your Actionable Notification Types ?

  6. Handling Notifications and Notification-Related Actions ?

  7. Modifying Content in Newly Delivered Notifications ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拴事,一起剝皮案震驚了整個(gè)濱河市沃斤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刃宵,老刑警劉巖衡瓶,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牲证,居然都是意外死亡鞍陨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門从隆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诚撵,“玉大人,你說我怎么就攤上這事键闺∈傺蹋” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵辛燥,是天一觀的道長筛武。 經(jīng)常有香客問我缝其,道長,這世上最難降的妖魔是什么徘六? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任内边,我火速辦了婚禮,結(jié)果婚禮上待锈,老公的妹妹穿的比我還像新娘漠其。我一直安慰自己,他們只是感情好竿音,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布和屎。 她就那樣靜靜地躺著,像睡著了一般春瞬。 火紅的嫁衣襯著肌膚如雪柴信。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天宽气,我揣著相機(jī)與錄音随常,去河邊找鬼。 笑死萄涯,一個(gè)胖子當(dāng)著我的面吹牛绪氛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窃判,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喇闸!你這毒婦竟也來了袄琳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤燃乍,失蹤者是張志新(化名)和其女友劉穎唆樊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刻蟹,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逗旁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舆瘪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片片效。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖英古,靈堂內(nèi)的尸體忽然破棺而出淀衣,到底是詐尸還是另有隱情,我是刑警寧澤召调,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布膨桥,位于F島的核電站蛮浑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏只嚣。R本人自食惡果不足惜沮稚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望册舞。 院中可真熱鬧蕴掏,春花似錦、人聲如沸环础。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽线得。三九已至饶唤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贯钩,已是汗流浹背募狂。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角雷,地道東北人祸穷。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像勺三,于是被迫代替她去往敵國和親雷滚。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容