Local and Remote Notification Programming Guide - Notifications in Your App

1,062 阅读34分钟

Notifications in Your App原文地址

Local and Remote Notifications Overview

本地通知和远程通知是通知用户何时新数据可用于应用程序的方法, 即使应用程序未在前台运行也是如此. 例如消息传递应用程序可能会在新消息到达时通知用户, 而日历应用程序可能会通知用户即将到来的预约. 本地通知和远程通知之间的区别很简单:

  • 使用本地通知, 应用程序在本地配置通知详细信息, 并将这些详细信息传递给系统,然后系统会在应用程序不在前台时处理通知的传递. iOS,tvOS和watchOS都支持本地通知
  • 使用远程通知, 可以使用公司的一台服务器通过Apple Push Notification服务将数据推送到用户设备. iOS, tvOS, watchOS和macOS都支持远程通知

本地通知和远程通知都需要添加代码以支持在应用程序中安排和处理通知的逻辑. 对于远程通知还必须提供一个服务器环境, 该环境能够从用户设备接收数据并将与通知相关的数据发送到Apple推送通知服务(APN), 这是Apple提供的服务, 用于处理远程通知的传递到用户设备

The User Notifications and User Notifications UI Frameworks

从iOS 10, watchOS 3和tvOS 10开始, 用户通知框架提供了一种一致的方式来调度和处理本地通知, 该框架除了管理本地通知外, 还支持处理远程通知, 尽管远程通知的配置仍然需要一些特定于平台的API. 因为它是一个单独的框架,所以可以在创建的应用程序和创建的扩展(例如WatchKit扩展)中使用它

注意 在macOS上配置和处理远程通知要求使用AppKit框架用于特定于平台的方法

用户通知框架(The User Notifications framework)还支持创建通知服务应用程序扩展, 使用该扩展程序可以在传递远程通知之前修改其内容. 如果在应用程序中包含通知服务应用程序扩展, 则系统会将传入的通知传递到扩展程序, 然后再将它们传递给用户. 可以使用此类扩展程序为应用程序的通知实施端到端加密, 在交付前修改通知内容, 或下载与通知有关的其他图像或媒体.

User Notifications UI框架是User Notifications框架的随附工具, 可自定义系统的通知界面的外观. 使用User Notifications UI框架定义了一个通知内容应用程序扩展, 它的工作是为视图控制器提供自定义内容, 以在通知界面中显示. 系统显示自定义视图控制器, 而不是默认系统界面. 可以使用这种扩展将媒体或动态内容合并到通知界面中

When to Use Local and Remote Notifications

由于iOS, tvOS和watchOS上的应用程序并非始终运行, 因此本地通知提供了一种在应用程序中要显示新信息时提醒用户的方法. 例如在后台从服务器提取数据的应用程序可以在收到一些有趣的信息时安排本地通知. 本地通知也非常适合需要在特定时间或到达特定地理位置时提醒用户的应用程序, 例如日历和待办事项列表应用程序

当部分或全部应用程序数据由公司的服务器管理时, 远程通知是合适的. 使用远程通知可以决定何时将通知推送到用户的设备. 例如消息传递应用程序将使用远程通知来让用户知道何时有新消息到达. 由于它们是从服务器发送的, 因此可以随时发送远程通知, 包括当应用程序不在用户设备上运行的时候

Local and Remote Notifications Look the Same to Users

对于用户而言, 在给定设备上显示时本地通知和远程通知之间没有区别. 两种类型的通知具有相同的默认外观, 由系统提供. 在某些情况下, 可以自定义外观, 但是大多数情况下, 可以选择希望通知用户的方式. 具体来说可以选择以下选项之一来发送通知:

  • 屏幕上的警报(alert)或横幅(banner)
  • 应用图标上的角标
  • 警报(alert), 横幅(banner)或角标随附的声音

在配置本地和远程通知时, 请选择最适合用户要传递的信息类型的交互类型. 例如待办事项列表应用程序可能有一个项目列表, 每个项目都有一个必须完成该项目的时间和一个优先级. 对于高优先级项目, 可能会在完成时间过后显示警报(alert), 以使用户知道他们应该立即对项目采取行动. 对于优先级较低的项目, 可以在应用程序的图标上加上角标或播放声音, 以提供更微弱的提醒以完成通知

警报(alert)可直接向用户显示消息, 但角标和声音的含义取决于应用程序. 开发者可能会使用不同的声音来传达特定类型的事件, 例如消息的到达或任务的完成. 角标始终包含一个数值, 通常用于指示等待用户注意的项目数. 如下图:

badged_app

始终谨慎使用本地和远程通知, 以避免惹恼用户. 该系统允许用户按应用启用和禁用警报, 声音和角标的显示. 尽管通知仍可能传递到应用程序, 但系统仅通过当前启用的选项通知用户, 如果用户完全禁用了通知, 则APN不会将应用的通知发送到用户的设备, 并且本地通知的调度始终会失败

Managing Your App’s Notification Support

必须在启动时配置应用程序以支持本地和远程通知. 具体来说如果执行以下任一操作, 则必须提前配置应用程序

  • 警报显示, 播放声音或角标其图标以响应到达的通知
  • 显示带有通知的自定义操作按钮

通常需要在应用程序启动完成之前执行所有配置. 在iOS和tvOS中这意味着不迟于UIApplication代理方法application:didFinishLaunchingWithOptions:配置通知支持. 在watchOS中, 配置该支持不迟于WKExtension代理方法applicationDidFinishLaunching方法. 开发者可以稍后再执行此配置, 但是在此配置完成之前, 必须避免安排针对应用的任何本地或远程通知

Requesting Authorization to Interact with the User

在iOS, tvOS和watchOS中, 应用程序必须授权显示警报, 播放声音或标记应用程序的图标以响应传入的通知. 请求授权会将对这些交互的控制权交给用户, 用户可以授予或拒绝请求. 用户还可以稍后在系统设置中为应用更改授权设置.

要请求授权, 请调用共享的UNUserNotificationCenter对象的requestAuthorizationWithOptions:completionHandler:方法. 如果应用程序已获得所有请求的交互类型的授权, 则系统会在完成处理程序块中将所授予的参数设置为YES. 如果不允许一种或多种交互类型, 则该参数为NO

请求授权的用户交互

UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
   completionHandler:^(BOOL granted, NSError * _Nullable error) {
      // Enable or disable features based on authorization.
}];

应用程序首次启动并调用requestAuthorizationWithOptions:completionHandler:方法时, 系统会提示用户授予或拒绝所请求的交互.由于系统保存了用户的响应, 因此在后续启动期间对该方法的调用不会再次提示用户

注意 用户可以使用系统设置随时更改应用的授权交互类型. 要精确确定可以使用的交互类型, 请调用UNUserNotificationCenter的getNotificationSettingsWithCompletionHandler:方法

Configuring Categories and Actionable Notifications

可操作的通知为用户提供了一种快速简便的方法响应通知来执行相关任务. 可操作通知的界面显示了用户可以点击的自定义操作按钮, 而不是强迫用户启动应用. 轻按后每个按钮都会关闭通知界面, 并将选定的操作转发到应用并立即处理. 将操作转发到应用程序可以避免用户在应用程序中进一步导航以执行操作从而节省时间

应用必须明确添加对可操作通知的支持. 在启动时, 应用程序必须注册一个或多个类别, 这些类别定义了应用程序发送的通知的类型. 与每个类别相关联的是用户在传递该类型的通知时操作通执行的操作. 每个类别最多可以有四个关联的动作, 尽管实际显示的动作数量取决于通知的显示方式和位置. 例如横幅显示不超过两个动作

注意 可操作通知仅支持iOS和watchOS

Registering the Notification Categories for Your App

类别定义了应用程序支持的通知类型, 并向系统传达了希望如何显示通知的方式. 可以使用类别将自定义操作与通知相关联, 并指定如何处理该类型的通知的选项. 例如使用类别选项来指定是否可以在CarPlay环境中显示通知

在启动时, 可以使用共享的UNUserNotificationCenter对象的setNotificationCategories:方法立即注册所有应用类别. 在调用该方法之前, 请创建一个或多个UNNotificationCategory类的实例, 并指定类别名称和显示该类型的通知时要使用的选项. 类别名称是应用程序的内部名称, 用户永远不会看到. 安排通知时, 可以在通知的有效内容中包含类别名称, 然后系统将使用该名称检索选项并显示通知.

下面显示了如何创建一个简单的UNNotificationCategory对象并将其注册到系统中. 此类别的名称为“GENERAL”,并配置有自定义关闭操作选项,当用户关闭通知界面时, 系统会在不执行任何其他操作的情况下通知应用程序

Creating and registering a notification category

UNNotificationCategory* generalCategory = [UNNotificationCategory categoryWithIdentifier:@"GENERAL"
                                                                                 actions:@[]
                                                                       intentIdentifiers:@[]
                                                                                 options:UNNotificationCategoryOptionCustomDismissAction];
 
// Register the notification categories.
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObjects:generalCategory, nil]];

无需为从应用中安排的所有通知分配类别. 但是如果app不包括类别, 则显示的通知中将没有任何自定义操作或配置选项

Adding Custom Actions to Your Categories

注册的每个类别最多可以包含四个自定义操作. 当类别包含自定义动作时, 系统会在通知界面中添加按钮, 每个按钮都带有一个自定义动作的标题. 如果用户点击自定义操作之一, 则系统会将相应的操作标识符发送到应用, 并根据需要启动应用

若要定义自定义动作, 请创建一个UNNotificationAction对象, 并将其添加到类别对象. 每个操作都包含相应按钮的标题字符串以及如何显示按钮和处理相关任务的选项. 当用户选择一个动作时, 系统会为应用提供该动作的标识符字符串, 然后可以使用该标识符字符串来识别要执行的任务

UNNotificationCategory* generalCategory = [UNNotificationCategory
      categoryWithIdentifier:@"GENERAL"
      actions:@[]
      intentIdentifiers:@[]
      options:UNNotificationCategoryOptionCustomDismissAction];
 
// Create the custom actions for expired timer notifications.
UNNotificationAction* snoozeAction = [UNNotificationAction
      actionWithIdentifier:@"SNOOZE_ACTION"
      title:@"Snooze"
      options:UNNotificationActionOptionNone];
 
UNNotificationAction* stopAction = [UNNotificationAction
      actionWithIdentifier:@"STOP_ACTION"
      title:@"Stop"
      options:UNNotificationActionOptionForeground];
 
// Create the category with the custom actions.
UNNotificationCategory* expiredCategory = [UNNotificationCategory
      categoryWithIdentifier:@"TIMER_EXPIRED"
      actions:@[snoozeAction, stopAction]
      intentIdentifiers:@[]
      options:UNNotificationCategoryOptionNone];
 
// Register the notification categories.
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObjects:generalCategory, expiredCategory,
      nil]];

尽管可以为每个类别最多指定四个自定义操作, 但是在某些情况下, 系统可能仅显示前两个操作. 例如当在横幅中显示通知时, 系统仅显示两个操作. 初始化UNNotificationCategory对象时, 请始终配置操作数组, 以便最相关的操作在该数组中排在首位

如果使用UNTextInputNotificationAction类配置操作, 则系统为用户提供一种输入文本作为通知响应的一部分的方法. 文本输入操作对于从用户收集自由格式文本很有用. 例如消息应用程序可以允许用户提供对消息的自定义响应. 当文本输入操作传递到应用程序进行处理时, 系统会将用户的响应打包在UNTextInputNotificationResponse对象中

Preparing Custom Alert Sounds

本地和远程通知可以指定传递通知时要播放的自定义警报声音. 可以将音频数据打包为aiff, wavcaf文件, 因为它们是由系统声音设备播放的,所以自定义声音必须采用以下音频数据格式之一

  • Linear PCM
  • MA4 (IMA/ADPCM)
  • µLaw
  • aLaw

将自定义声音文件放的应用程序包中或应用程序容器目录的Library/Sounds文件夹中, 自定义声音播放时必须在30秒以内. 如果自定义声音超过该限制, 则会播放默认的系统声音

可以使用afconvert工具转换声音. 例如要将16位线性PCM系统声音Submarine.aiff转换为CAF文件中的IMA4音频, 请在终端应用程序中使用以下命令

afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v

Managing Your App’s Notification Settings

由于用户可以随时更改应用的通知设置, 因此可以使用共享的UNUserNotificationCenter对象的getNotificationSettingsWithCompletionHandler:方法随时获取应用的授权状态, 该方法返回一个UNNotificationSettings对象, 其内容反映了应用的当前授权状态和当前通知环境

使用UNNotificationSettings对象中的信息来调整应用程序的通知相关代码. 可以将应用程序的警报, 角标和声音授权设置传达给提供者(provider), 以便它可以调整任何远程通知有效内容中包含的选项.(provider是配置和使用APN的服务器, 可以对其进行部署和管理). 可以使用其他设置来调整计划和配置通知的方式

Managing Delivered Notifications

当应用或用户未直接处理本地和远程通知时, 它们会显示在“通知中心”中, 以便以后查看. 使用共享的UNUserNotificationCenter对象的getDeliveredNotificationsWithCompletionHandler:方法来获取仍在通知中心显示的通知列表. 如果发现任何现在过时且不应显示给用户的通知, 则可以使用removeDeliveredNotificationsWithIdentifiers:方法将其删除

Scheduling and Handling Local Notifications

本地通知提供了一种在应用程序可能未运行时提醒用户的方法. 开发者可以在应用程序在前台或后台运行时调度一次本地通知. 调度通知后, 系统负责在适当的时间将通知传递给用户. 应用无需运行系统即可传递通知

如果应用未运行, 或者在后台运行, 则系统会直接向用户显示本地通知. 系统可以通过警报面板或横幅, 声音或角标来通知用户. 如果应用程序提供了通知内容应用程序扩展, 则系统甚至可以使用自定义界面来提醒用户. 如果通知到达时, 应用程序处于前台, 则系统会为应用程序提供内部处理通知的机会.

注意 本地通知仅在iOS, watchOS和tvOS中受支持. 在macOS中, 应用程序不需要本地通知即可在后台运行时标记其图标,播放声音或显示警报. 这些功能AppKit框架已支持

Configuring a Local Notification

配置本地通知的步骤如下:

  1. 创建并配置带有通知详细信息的UNMutableNotificationContent对象

  2. 创建一个UNCalendarNotificationTrigger, UNTimeIntervalNotificationTriggerUNLocationNotificationTrigger对象来描述传递通知的条件

  3. 使用内容和触发器信息创建一个UNNotificationRequest对象

  4. 调用addNotificationRequest:withCompletionHandler:方法来调度通知

在创建通知内容时, 请填写UNMutableNotificationContent对象的属性, 以反映希望与用户进行交互的类型. 例如要显示警报时, 请填写标题和正文(title and body)属性. 系统使用提供的信息来确定如何与用户进行交互. 还可以在处理已传递到应用程序的本地通知时使用此对象中的数据

创建通知内容之后, 创建一个触发器对象, 该对象定义何时传递通知. 用户通知框架提供了基于时间的触发和基于位置的触发. 使用所需条件配置触发器, 并使用该对象和您的内容创建UNNotificationRequest对象

下面的代码显示了如何创建和配置与警报相关的本地通知, 使用UNCalendarNotificationTrigger可使通知在特定的日期或时间传递. 在此示例中, 这是早上7:00的闹钟

UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"Wake up!" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:@"Rise and shine! It's morning time!"
        arguments:nil];
 
// Configure the trigger for a 7am wakeup.
NSDateComponents* date = [[NSDateComponents alloc] init];
date.hour = 7;
date.minute = 0;
UNCalendarNotificationTrigger* trigger = [UNCalendarNotificationTrigger
       triggerWithDateMatchingComponents:date repeats:NO];
 
// Create the request object.
UNNotificationRequest* request = [UNNotificationRequest
       requestWithIdentifier:@"MorningAlarm" content:content trigger:trigger];

通过为UNNotificationRequest对象提供标识符, 可以在调度本地通知后识别本地通知. 还可以使用标识符稍后查找待处理的请求, 并在交付之前取消它们

Assigning Custom Actions to a Local Notification

要在本地通知的界面中显示自定义操作, 请在配置过程中将注册的类别标识符分配给UNMutableNotificationContent对象的categoryIdentifier属性. 系统使用类别信息来确定要在通知界面中包括哪些操作按钮(如果有), 在调度通知请求之前, 必须为该属性分配一个值

下面的代码显示了如何为本地通知指定类别标识符. 在此示例中, “TIMER_EXPIRED”字符串表示在启动时定义的类别, 其中包括两个自定义操作

UNNotificationContent *content = [[UNNotificationContent alloc] init];
// Configure the content. . .
 
// Assign the category (and the associated actions).
content.categoryIdentifier = @"TIMER_EXPIRED";
 
// Create the request and schedule the notification.

Adding a Sound to the Notification Content

如果希望本地通知在传递时播放声音, 请为UNMutableNotificationContent对象的sound属性分配一个值. 可以使用UNNotificationSound对象指定声音, 该对象可以播放自定义声音或默认通知声音. 自定义声音必须先保存在用户设备上, 然后才能播放. 将通知的声音文件存储在应用程序bundle中, 或下载它们, 然后将其存储在应用程序容器目录的Library/Sounds子目录中

要播放默认声音, 请创建声音文件并将其分配给App的通知内容. 例如:

content.sound = [UNNotificationSound defaultSound];

指定自定义声音时, 仅指定要播放的声音文件的文件名. 如果系统找到了具有开发者提供的名称的合适的声音文件, 则在传递通知时会播放该声音. 如果系统找不到合适的声音文件, 则播放默认声音

content.sound = [UNNotificationSound soundNamed:@"MySound.aiff"];

Scheduling Local Notifications for Delivery

要安调度地通知的发送, 请创建UNNotificationRequest对象, 然后调用UNUserNotificationCenteraddNotificationRequest:withCompletionHandler:方法. 系统异步调度本地通知, 在调度完成或发生错误时调用完成处理程序块. 下列代码显示了如何安排本地通知的交付


// Create the request object.
UNNotificationRequest* request = [UNNotificationRequest
       requestWithIdentifier:@"MorningAlarm" content:content trigger:trigger];
 
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
   if (error != nil) {
       NSLog(@"%@", error.localizedDescription);
   }
}];

调度本地通知将保持活动状态, 直到系统取消调度的通知或明确取消它们为止. 除非通知的触发器配置为重复执行, 否则系统会在通知发送后自动取消调度. 要在传递单个通知之前取消它, 或者要取消重复的通知, 请调用UNUserNotificationCenterremovePendingNotificationRequestsWithIdentifiers:方法. 被取消的通知必须为其ID分配一个UNNotificationRequest对象. 要取消所有挂起的本地通知, 无论它们是否具有请求标识符, 请改为调用removeAllPendingNotificationRequests方法

Responding to the Delivery of Notifications

当应用未运行或处于后台时, 系统会使用开发者指定的交互方式自动传递本地和远程通知. 如果用户选择了一项操作, 或选择了一种标准的互动方式, 则系统会将开发者的选择通知应用. 然后编写代码使用该选择来执行其他任务. 如果应用程序在前台运行, 则通知会直接传递到应用程序. 然后可以决定是安静地处理通知还是警告用户

要响应通知的传递, 必须为共享的UNUserNotificationCenter对象实现代理方法. 代理对象必须遵守UNUserNotificationCenterDelegate协议, 通知中心使用该协议将通知信息传递到应用程序. 如果通知包含自定义操作,则需要代理去处理

重要 必须将代理分配给共享的UNUserNotificationCenter对象, 然后才能启动应用程序或应用程序扩展. 否则可能会阻止应用正确处理通知

Handling Notifications When Your App Is in the Foreground

如果在应用程序处于前台时收到通知, 则可以使该通知静音或告诉系统继续显示通知界面. 默认情况下, 系统会将前台应用程序的通知静音, 将通知的数据直接传递到应用程序. 开发者可以使用通知数据直接更新应用界面. 例如, 如果新的体育比分到来, 只需在界面中更新该信息即可

如果希望系统继续显示通知界面, 请为UNUserNotificationCenter提供一个代理对象, 并实现userNotificationCenter:willPresentNotification:withCompletionHandler:方法. 实现此方法仍应处理通知数据. 完成后, 处理要系统使用的交付选项(如果有)执行提供的完成处理程序块. 如果未指定任何选项, 则系统将使通知静音, 下列代码示了此方法的示例实现, 该方法告诉系统播放声音. 通知有效标识对应的声音

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
        willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
   // Update the app interface directly.
 
    // Play a sound.
   completionHandler(UNNotificationPresentationOptionSound);
}

当应用程序在后台或未运行时, 系统不会调用userNotificationCenter:willPresentNotification:withCompletionHandler:方法. 在那些情况下, 系统会根据通知本身中的信息向用户发出警报, 开发者仍然可以使用UNUserNotificationCenter对象的getDeliveredNotificationsWithCompletionHandler:方法确定是否已传递通知

Responding to the Selection of a Custom Action

当用户从通知界面中选择自定义操作时, 系统会将用户的选择通知应用. 对自定义操作的响应打包在UNNotificationResponse对象中, 并传递给应用程序的共享UNUserNotificationCenter对象的代理. 要接收响应, 代理对象必须实现userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:方法. 开发者对该方法的实现必须能够处理应用或应用扩展程序支持的所有自定义操作

如果收到响应时应用程序或应用程序扩展名未在运行, 则系统会在后台启动应用程序或应用程序扩展名以处理响应. 使用提供的后台时间来更新数据结构和应用界面, 以反映用户的选择. 不要用时间来执行与自定义操作的处理无关的任务

下列代码显示了具有多个类别和自定义操作的计时器应用程序的响应处理程序方法的实现. 该实现同时使用actioncategoryIdentifier属性来确定适当的操作过程

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
           didReceiveNotificationResponse:(UNNotificationResponse *)response
           withCompletionHandler:(void (^)(void))completionHandler {
    if ([response.notification.request.content.categoryIdentifier isEqualToString:@"TIMER_EXPIRED"]) {
        // Handle the actions for the expired timer.
        if ([response.actionIdentifier isEqualToString:@"SNOOZE_ACTION"])
        {
            // Invalidate the old timer and create a new one. . .
        }
        else if ([response.actionIdentifier isEqualToString:@"STOP_ACTION"])
        {
            // Invalidate the timer. . .
        }
 
    }
 
    // Else handle actions for other notification types. . .
}

Handling the Standard System Actions

在系统的通知界面中, 用户可以显式关闭通知界面或启动应用, 而无需选择任何自定义操作. 退出界面需要点击相应的按钮或直接关闭界面. 忽略通知或将通知标语甩开并不表示明确关闭. 触发系统操作后, 用户通知中心会将其报告给其代理执行`userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:方法. 传递给该方法的响应对象包含以下操作标识符之一:

UNNotificationDismissActionIdentifier使开发者知道用户无需选择自定义操作即可显式关闭通知界面

UNNotificationDefaultActionIdentifier使开发者知道用户在未选择自定义操作的情况下启动了您的应用程序

以与处理标准系统操作相同的方式来处理其他操作. 如下显示了userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:方法的模板

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
          didReceiveNotificationResponse:(UNNotificationResponse *)response
          withCompletionHandler:(void (^)(void))completionHandler {
   if ([response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
       // The user dismissed the notification without taking action.
   }
   else if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
       // The user launched the app.
   }
 
   // Else handle any custom actions. . .
}

Configuring Remote Notification Support

通过支持远程通知, 即使应用程序未运行, 也可以向其用户提供最新信息. 为了能够接收和处理远程通知, 应用必须:

  • 启用远程通知
  • 向Apple推送通知服务(APN)注册并接收特定于应用程序的设备令牌
  • 将设备令牌发送到开发者的通知provider序服务器
  • 实现对处理传入的远程通知的支持

本章介绍了这些在应用程序中需要实现的步骤. 有关提providers的更多信息,这些providers是部署和管理的用于构建通知请求并将通知请求发送到APN的服务器, 请阅读APN概述.

注意

APNs可以向未运行的应用程序传递远程通知的功能要求该应用程序至少已启动一次

在iOS设备上, 如果用户使用应用程序多任务UI强制退出应用程序, 则在用户重新启动应用程序之前, 该应用程序不会收到远程通知.

Enabling the Push Notifications Capability

为了使应用程序能够处理远程通知, 它必须具有与APN对话的权限. 可以使用Xcode项目的“Capabilities”(功能)窗格将此权限添加到应用中, 如Xcode帮助中的“启用推送通知”中所述

没有权限的应用在App Store审核过程中将被拒绝. 在测试期间, 尝试在没有权限的情况下向APN注册会返回错误

Registering to Receive Remote Notifications

每次应用启动时, 它都必须向APN注册. 使用的方法因平台而异, 但在所有情况下其工作方式如下

  1. 应用要求向APN注册
  2. 成功注册后, APNs将特定于应用程序的设备令牌发送到设备
  3. 系统通过调用应用程序代理的方法将设备交付给应用程序
  4. 应用将设备令牌发送到应用的关联provider服务器

特定于应用程序的设备令牌在全球范围内是唯一的, 并且标识一个应用程序-设备组合. 在应用中从APN收到设备令牌后, 开发者有责任打开与provider之间的网络连接. 然后在应用中, 开发者还有责任转发设备令牌以及要发送给provider服务器的任何其他相关数据. 之后provider服务器将远程通知请求发送到APN时, 它必须包括设备令牌(device token)以及通知数据(notification payload)

切勿在应用程序中缓存设备令牌, 而是在需要时从系统中获取它们. 当某些事件发生情况下, APN会向应用发布新的设备令牌, 保证设备令牌是不同的. 例如当用户从备份中还原设备时, 当用户在新设备上安装应用程序以及当用户重新安装操作系统时, 获取令牌不能依赖缓存, 可确保拥有provider与APN通信所需的当前设备令牌. 当尝试获取设备令牌但未更改时, fetch方法将很快返回

重要 设备令牌更改后, 用户必须先启动应用, 然后APN才能再次向设备传递远程通知

在watchOS上运行的应用不会显式注册远程通知. 相反他们依靠配对的iPhone转发远程通知以在手表上显示. 当iPhone锁定(或屏幕处于睡眠状态)并且Apple Watch戴在用户的手腕上且未锁定时, 将进行远程通知的转发

Obtaining a Device Token in iOS and tvOS

在iOS和tvOS中, 可以通过调用UIApplication对象的registerForRemoteNotifications方法来为应用启动APNs注册, 作为正常启动序列的一部分, 在启动时调用此方法. 应用程序第一次调用此方法时, 该应用程序对象会和APN通信并请求特定于应用程序的设备令牌. 然后系统根据成功或失败异步调用以下两个应用程序代理方法:

  • 成功发行特定于应用程序的设备令牌后, 系统将调用application:didRegisterForRemoteNotificationsWithDeviceToken:方法, 实现此方法以接收令牌并将其转发给开发者的服务器
  • 错误时系统将调用application:didFailToRegisterForRemoteNotificationsWithError:方法, 实现此方法以响应APNs注册错误

重要 APNs设备令牌的长度是可变的, 不要硬编码它们的大小

成功注册APN之后, 仅当设备令牌已更改时, 应用程序对象才会与APN通信, 除此之外, 调用registerForRemoteNotifications方法将导致对应用程序的调用didRegisterForRemoteNotificationsWithDeviceToken:方法, 该方法可快速返回现有令牌

注意 如果在应用程序运行期间设备令牌发生更改, 则该应用程序对象会再次调用application:didRegisterForRemoteNotificationsWithDeviceToken:代理方法将更改通知到开发者

下列代码显示了如何为iOS或tvOS应用程序获取设备令牌. 应用程序代理在其常规启动时间设置中调用registerForRemoteNotifications方法, 收到设备令牌后, application:didRegisterForRemoteNotificationsWithDeviceToken:方法会使用自定义方法将其转发给应用程序的关联服务器. 如果注册期间发生错误, 则该应用会暂时禁用与远程通知相关的所有功能. 收到有效的设备令牌后, 将重新启用这些功能

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Configure the user interactions first.
    [self configureUserInteractions];
 
   // Register for remote notifications.
    [[UIApplication sharedApplication] registerForRemoteNotifications];

    // ...
    return YES;
}
 
// Handle remote notification registration.
- (void)application:(UIApplication *)app
        didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
    // Forward the token to your provider, using a custom method.
    [self enableRemoteNotificationFeatures];
    [self forwardTokenToServer:devTokenBytes];
}
 
- (void)application:(UIApplication *)app
        didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
    // The token is not currently available.
    NSLog(@"Remote notification support is unavailable due to error: %@", err);
    [self disableRemoteNotificationFeatures];
}

如果蜂窝或Wi-Fi连接不可用, 则不会调用application:didRegisterForRemoteNotificationsWithDeviceToken:方法和`application:didFailToRegisterForRemoteNotificationsWithError:方法, 对于Wi-Fi连接, 当设备无法通过配置的端口与APN连接时, 有时会发生这种情况. 如果发生这种情况, 用户可以移动到另一个没有阻塞所需端口的Wi-Fi网络. 在带有蜂窝无线电的设备上, 用户还可以等待直到蜂窝数据服务可用

application:didFailToRegisterForRemoteNotificationsWithError:实现中, 使用错误对象禁用与远程通知相关的任何功能. 由于无论如何都不会收到通知, 因此最好进行适当降级,并避免进行远程通知所需的任何本地工作. 如果以后有远程通知可用, 则app对象通过调用代理的application:didRegisterForRemoteNotificationsWithDeviceToken:方法来通知

Obtaining a Device Token in macOS

在macOS中, 可以通过调用NSApplication对象的registerForRemoteNotificationTypes:方法来获取应用程序的设备令牌. 建议在启动时调用此方法, 这是正常启动序列的一部分. 应用程序第一次调用此方法时, 应用程序对象向APN请求令牌. 首次调用后, 仅当设备令牌更改时,app对象才会与APN通信. 除此, 它将快速返回现有令牌

应用程序对象在成功或不成功获取设备令牌时异步通知其代理. 可以使用这些代理回调来处理设备令牌或处理出现的任何错误. 必须实现以下代理方法以跟踪注册是否成功

  • 使用以下应用程序didRegisterForRemoteNotificationsWithDeviceToken:接收设备令牌并将其转发给开发者的(provider)服务器
  • 使用application:didFailToRegisterForRemoteNotificationsWithError:来响应错误

注意 如果在应用程序运行期间设备令牌发生更改, 则该应用程序对象将再次调用适当的代理方法以来改通知开发者

以下代码显示了如何为macOS应用获取设备令牌. 应用程序代理在其常规启动时间设置中调用registerForRemoteNotificationTypes:方法, 并传递打算使用的交互类型. 收到设备令牌后, application:didRegisterForRemoteNotificationsWithDeviceToken:方法会使用自定义方法将其转发给应用程序的关联(provider)服务器. 如果注册期间发生错误, 则该应用会暂时禁用与远程通知相关的所有功能. 收到有效的设备令牌后, 将重新启用这些功能

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
    // Configure the user interactions first.
    [self configureUserInteractions];
 
    [NSApp registerForRemoteNotificationTypes:(NSRemoteNotificationTypeAlert | NSRemoteNotificationTypeSound)];
}
 
- (void)application:(NSApplication *)application
        didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // Forward the token to your provider, using a custom method.
    [self forwardTokenToServer:deviceToken];
}
 
- (void)application:(NSApplication *)application
        didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"Remote notification support is unavailable due to error: %@", error);
    [self disableRemoteNotificationFeatures];
}

Handling Remote Notifications

用户通知框架提供了可在iOS, watchOS和tvOS应用中使用的统一API, 并支持与本地和远程通知相关的大多数任务. 这是开发者可以在此框架下执行的一些任务示例

  • 如果应用位于前台, 则可以直接接收通知并使其静音
  • 如果应用程序在后台运行或未运行
    • 当用户选择与通知关联的自定义操作时可以做出响应
    • 当用户关闭通知或启动应用时可以做出响应

应用程序通过其应用程序代理接收远程通知的payload, 当远程通知到达时, 系统会在应用程序处于后台时正常处理用户交互. 在iOS和tvOS中, 系统将通知payload传递到应用程序代理的application:didReceiveRemoteNotification:fetchCompletionHandler:方法. 在macOS中, 系统将payload交付给应用程序委托的application:didReceiveRemoteNotification:方法, 开发者可以使用这些方法来检查payload并执行任何相关任务. 例如收到后台更新远程通知后, 可以开始为应用下载新内容

Modifying and Presenting Notifications

可以使用附加应用信息修改到达通知的内容或显示方式. 要在传递远程通知之前修改其内容, 请使用Notification Service应用程序扩展. 要更改通知内容在屏幕上的显示方式, 请使用通知内容应用扩展程序

Modifying the Payload of a Remote Notification

使用通知服务应用程序扩展, 在将远程通知传递给用户之前, 先对其进行修改. 远程通知源自服务器, 该服务器可以控制通知的配置和内容, 服务扩展使应用可以在将服务器提供的payload数据呈现给用户之前对其进行更改. 使用服务扩展来实现以下类型的行为

  • 解密以加密格式传送的数据
  • 下载图像或其他媒体文件, 并将它们添加为通知的附件
  • 更改通知的正文或标题文本
  • 在通知中添加线程标识符或修改通知的userInfo字典的内容

将通知服务应用扩展添加到iOS应用

  • 在Xcode中,选择 New > Target以将新目标添加到项目中
  • iOS > Application Extension部分中, 选择Notification Service Extension target
  • 点击Next
  • 指定应用扩展程序的名称和其他详细信息
  • 点击Finish

Xcode将预配置的目标添加到应用项目

Xcode提供的默认通知服务扩展target包含UNNotificationServiceExtension类的子类供开发者修改. 使用didReceiveNotificationRequest:withContentHandler:方法创建和配置新的UNMutableNotificationContent对象, 开发者可以对新的内容对象进行任何更改, 从而替换部分或全部原始内容值. 完成后使用新的内容对象调用提供的完成处理程序. 系统将新内容集成到通知中, 并将其交付给用户

系统提供了有限的时间来修改通知并调用提供的完成处理程序, 因此应该快速执行所有任务. 如果didReceiveNotificationRequest:withContentHandler:方法花费太长时间来调用完成处理程序, 则系统将调用serviceExtensionTimeWillExpire方法, 提供最后一次完成修改的机会. 如果没有及时调用完成处理程序, 系统将显示通知的原始内容

必须精心设计服务器发送的远程通知, 以支持通过Notification Service应用程序扩展进行修改. 无需修改的通知将直接发送给用户. 为远程通知创建payload时, 服务器应执行以下操作

  • 包含mutable-content字段的置为1的键值对
  • 包含alert字典, 其中有titlebody键的内容

Presenting Notifications Using a Custom Interface on iOS

使用通知内容应用扩展程序可为应用通知显示自定义用户界面. 可以使用这种类型的扩展来合并自定义内容或使用与默认界面不同的布局. 例如可以使用这种扩展名来显示与通知内联的图像或媒体文件

通知内容应用程序扩展程序支持呈现与特定category关联的本地和远程通知. 可以使用UNNotificationContent对象的categoryIdentifier指定本地通知的category. 对于远程通知, 服务器在payload的aps词典中包含一个category键,并带有适当的值. 当具有该category的通知到达时, 系统将从扩展程序中加载视图控制器, 并将内容合并到系统界面中. 开发者可以使用通知内容在视图控制器出现在屏幕上之前对其进行配置

向iOS应用添加通知内容应用扩展

  • 在Xcode中,选择 New > Target以将新目标添加到项目中
  • iOS > Application Extension部分中, 选择Notification Content Extension target
  • 点击Next
  • 指定应用扩展程序的名称和其他详细信息
  • 点击Finish

Xcode将预配置的目标添加到应用项目

初始化通知内容应用程序扩展目标配置为呈现与单个category关联的通知。必须修改目标以指定每个扩展要支持的通知category. 可以使用目标的Info.plist文件中的UNNotificationExtensionCategory键指定category. 将应用程序在启动时注册的UNNotificationCategory对象的identifier属性的键值设置为相同的字符串

在应用扩展程序中支持多个通知category

  1. 选择通知内容扩展项目的Info.plist文件
  2. 展开NSExtension词典以查看与扩展相关的键
  3. 展开NSExtensionAttributes字典
  4. UNNotificationExtensionCategory键的类型更改为Array
  5. 为扩展处理的通知category添加一个条目

可以在iOS应用包中包含多个通知内容应用扩展. 系统期望仅一个扩展支持给定category, 因此必须为每个扩展配置UNNotificationExtensionCategory键, 并为其使用不同的值集

Presenting Notifications Using a Custom Interface on watchOS

WatchKit框架直接支持使用自定义界面呈现通知. 一个WatchKit扩展程序可能包括一个或多个通知界面控制器, 以在应用程序收到通知时显示. 使用这些接口控制器来呈现通知的内容. 有关如何实现通知接口控制器的信息, 查看App Programming Guide for watchOS

理解如有错误 望指正 转载请说明出处