iOS 15 Notifications

通知摘要

通知摘要是一项可选功能,允许用户停止接收特定应用程序的实时推送通知。相反,系统会存储为这些应用程序收到的通知,并在锁屏上的摘要中显示它们。系统可以在一天中的不同时刻显示这些摘要,具体取决于用户的偏好。

iOS 不会自动将应用程序添加到通知摘要中。首次启用该功能时,用户需要从系统设置中手动添加:

image

打开通知摘要后,推送权限提示将提供两个选项来处理应用程序的通知:

  • Allow Immediate Notifications”:收到通知后立即显示。

  • Add to Scheduled Summary”:收到通知后稍后显示在通知摘要中。

image

请注意,iOS 不提供检测用户选择的哪种通知处理方式(立即通知与预定摘要)。

新增 API

  • relevanceScore 指定应在此摘要中突出显示哪些通知。

专注模式

iOS 15 的新专注模式是当前勿扰模式的改进和扩展。用户可以创建不同的“专注模式”来过滤他们在活动期间(如:工作、驾驶等)收到的通知。从系统设置中,用户可以创建“工作”焦点并将其设置在同事、家人和特定应用程序通知到达时立即通知。

image

专注模式激活时,其他联系人和应用程序的通知将仅显示在通知中心,如下所示:

image

相关 API

  • UIFocusDebugger 用于调试与焦点相关交互的运行时对象。https://www.jianshu.com/p/f5c4f4b137fd

新的通知中断级别

除了新的通知摘要和专注模式之外,iOS 15 还为推送通知引入了两个新的中断级别:passivetime-sensitive

总共有四个不同的中断级别:

  • Passive 被动:对不需要立即关注的通知使用被动模式(如:营销活动等)。被动通知不会触发声音、振动和亮屏。

  • Active 活动:这是默认的中断级别(如:新闻等)。

  • Time-Sensitive 时间敏感:对需要立即关注的通知(如:帐户安全问题、快递送达等)使用时间敏感中断级别。该中断级别不要用于发送营销通知,因为这些通知可能会突破系统控制(通知摘要和专注模式)。

  • Critical 严重:严重中断级别用于需要立即关注的非常重要的通知(如:恶劣天气等)。这种使用必须由 Apple 明确允许并具有特殊权利。

image

请注意,时间敏感严重中断级别都可以突破通知摘要任何专注模式。iOS 将显示刚刚收到的通知:

image

如果 App 的时间敏感通知不经常交互,iOS 会从锁定屏幕提示用户,让用户为 App 禁用时间敏感的通知。用户也可以从系统设置中禁用:

image

发送时间敏感的通知

先决条件

为了使用时间敏感通知,App 需要将“时间敏感通知”功能添加到 Xcode 项目中。

image

设置推送通知数据

时间敏感的中断级别可以使用“interruption-level” payload key:

{"aps":{"interruption-level":"time-sensitive"}}

新增 API

  • UNNotificationInterruptionLevel 中断级别枚举。
  • timeSensitiveSetting App 是否有时间敏感权限。

通知操作图标

通知操作现在可以包含图标以更好的表达相关操作。这些图标可以是 SFSymbol 系统图像,也可以是 App 提供的自定义图像。为了添加图标,Apple 提供了新的 API UNNotificationActionIcon 对象。Action Icon 对象可以使用系统或模板图像名称进行初始化,然后使用包含 icon 参数的新初始化方法添加到相应的 UNNotificationAction 中。您不需要在名称中指定文件扩展名或大小修饰符,因为会根据系统和可用图像资源自动检索正确的大小。

image
image

通讯通知

Apple 添加了将应用程序的通知区分为通信通知的功能。这些通知现在将包含发送它们的联系人的图像或头像,并且可以与 SiriKit 集成,以便 Siri 可以根据常用联系人智能地为通信操作提供快捷方式和建议。例如,当用户为焦点模式设置允许的联系人或从您的应用拨打电话时。 Siri 将根据您的应用程序提供的意图数据智能地推荐联系人。

要使用通信通知,应用程序需要在 Xcode 中向其应用程序添加 Communication Notifications 功能,并使用实现了 UNNotificationContentProviding 协议的 Intent 对象更新 App Notification Service Extension 的通知内容。目前两个可用的实现是 INSendMessageIntent 和 INStartCallIntent。

image

APNs 实现通讯通知

项目中添加 Notification Service Extension,将以下 key value 添加到 Notification Service Extension 的 Info.plist 中。

image

NotificationService.didReceive 中编写代码向通知对象添加附加信息。(每次 Apple APNs 在通知用户前,都会调用此方法)

import UserNotifications
import Intents
import UIKit
class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        if let bestAttemptContent = bestAttemptContent {
            // ...
        }
    }
}

需要使用 INPerson 装载发送者信息,这些信息可以通过 APNs 通知传给 App。如:

{
    "aps": {
        "alert": {
            "body": "Hello world! This is a test message.",
            "title": "@Neko"
        },
    },
    "sender_id": "1",
    "sender_name": "NekoNeko",
    "sender_image_url": "https://xxxx.com/xxx.jpg",
    "sender_nickname": "@Neko",
    "sender_email": "[email protected]",
    "chat_session_id": "chat_1"
} 

然后,可以通过使用 INPersonINSendMessageIntent 将发送者信息添加到推送通知中。

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"

            /* Use the custom information included in this notification from the chat server to retrive the chat participant's information. - This is the information of the sender of the message. - Providing a user's accruate name helps the iOS system match this user with a contact in the system contact app. */
            if let senderAccountID = bestAttemptContent.userInfo["sender_id"] as? String,
               let senderName = bestAttemptContent.userInfo["sender_name"] as? String,
               let senderImageURLString = bestAttemptContent.userInfo["sender_image_url"] as? String,
               let senderImageURL = URL(string: senderImageURLString),
               let senderDisplayName = bestAttemptContent.userInfo["sender_nickname"] as? String,
               let senderEmailAddr = bestAttemptContent.userInfo["sender_email"] as? String,
               let chatSessionID = bestAttemptContent.userInfo["chat_session_id"] as? String
            {

                // You can also use the sender's phone number to initialize the `INPersonHandle` object. This will help the iOS system to match this sender with a contact.
                // TODO: - Here you need to download the image data from the URL. In this demo, we are using a system image instead.
                let messageSender = INPerson(
                    // email or phone number
                    personHandle: INPersonHandle(value: senderEmailAddr, type: .emailAddress),
                    nameComponents: try? PersonNameComponents(senderName),
                    displayName: senderDisplayName,
                    image: INImage(imageData: UIImage(systemName: "applelogo")!.pngData()!),
                    contactIdentifier: nil,
                    customIdentifier: senderAccountID,
                    isMe: false,
                    suggestionType: .instantMessageAddress
                )

                let intent = INSendMessageIntent(recipients: nil,
                                                 outgoingMessageType: .outgoingMessageText,
                                                 content: bestAttemptContent.body,
                                                 speakableGroupName: INSpeakableString(spokenPhrase: senderDisplayName),
                                                 conversationIdentifier: chatSessionID,
                                                 serviceName: nil,
                                                 sender: messageSender,
                                                 attachments: nil)

                let interaction = INInteraction(intent: intent, response: nil)
                interaction.direction = .incoming
                interaction.donate(completion: nil)
                do {
                    let messageContent = try request.content.updating(from: intent)
                    contentHandler(messageContent)
                } catch {
                    print(error.localizedDescription)
                }

            }

            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

}

相关资料

  • iOS 15 Notification Changes from WWDC 21
  • Understanding and managing iOS 15 time-sensitive interruption level
  • iOS 15: SNSアプリ向けのインテントにより、通知UIやSiriからの提案を改善。 - Qiita
  • UserNotifications iOS 14.5 to 15.0 API Differences

你可能感兴趣的:(iOS 15 Notifications)