通知摘要
通知摘要是一項(xiàng)可選功能,允許用戶停止接收特定應(yīng)用程序的實(shí)時(shí)推送通知。相反,系統(tǒng)會存儲為這些應(yīng)用程序收到的通知,并在鎖屏上的摘要中顯示它們。系統(tǒng)可以在一天中的不同時(shí)刻顯示這些摘要,具體取決于用戶的偏好。
iOS 不會自動將應(yīng)用程序添加到通知摘要中。首次啟用該功能時(shí),用戶需要從系統(tǒng)設(shè)置中手動添加:
打開通知摘要后,推送權(quán)限提示將提供兩個選項(xiàng)來處理應(yīng)用程序的通知:
“Allow Immediate Notifications”:收到通知后立即顯示。
“Add to Scheduled Summary”:收到通知后稍后顯示在通知摘要中。
請注意,iOS 不提供檢測用戶選擇的哪種通知處理方式(立即通知與預(yù)定摘要)。
新增 API
- relevanceScore 指定應(yīng)在此摘要中突出顯示哪些通知。
專注模式
iOS 15 的新專注模式是當(dāng)前勿擾模式的改進(jìn)和擴(kuò)展。用戶可以創(chuàng)建不同的“專注模式”來過濾他們在活動期間(如:工作、駕駛等)收到的通知。從系統(tǒng)設(shè)置中,用戶可以創(chuàng)建“工作”焦點(diǎn)并將其設(shè)置在同事、家人和特定應(yīng)用程序通知到達(dá)時(shí)立即通知。
專注模式激活時(shí),其他聯(lián)系人和應(yīng)用程序的通知將僅顯示在通知中心,如下所示:
相關(guān) API
- UIFocusDebugger 用于調(diào)試與焦點(diǎn)相關(guān)交互的運(yùn)行時(shí)對象。http://www.itdecent.cn/p/f5c4f4b137fd
新的通知中斷級別
除了新的通知摘要和專注模式之外,iOS 15 還為推送通知引入了兩個新的中斷級別:passive 和 time-sensitive。
總共有四個不同的中斷級別:
Passive 被動:對不需要立即關(guān)注的通知使用被動模式(如:營銷活動等)。被動通知不會觸發(fā)聲音、振動和亮屏。
Active 活動:這是默認(rèn)的中斷級別(如:新聞等)。
Time-Sensitive 時(shí)間敏感:對需要立即關(guān)注的通知(如:帳戶安全問題、快遞送達(dá)等)使用時(shí)間敏感中斷級別。該中斷級別不要用于發(fā)送營銷通知,因?yàn)檫@些通知可能會突破系統(tǒng)控制(通知摘要和專注模式)。
Critical 嚴(yán)重:嚴(yán)重中斷級別用于需要立即關(guān)注的非常重要的通知(如:惡劣天氣等)。這種使用必須由 Apple 明確允許并具有特殊權(quán)利。
請注意,時(shí)間敏感和嚴(yán)重中斷級別都可以突破通知摘要和任何專注模式。iOS 將顯示剛剛收到的通知:
如果 App 的時(shí)間敏感通知不經(jīng)常交互,iOS 會從鎖定屏幕提示用戶,讓用戶為 App 禁用時(shí)間敏感的通知。用戶也可以從系統(tǒng)設(shè)置中禁用:
發(fā)送時(shí)間敏感的通知
先決條件
為了使用時(shí)間敏感通知,App 需要將“時(shí)間敏感通知”功能添加到 Xcode 項(xiàng)目中。
設(shè)置推送通知數(shù)據(jù)
時(shí)間敏感的中斷級別可以使用“interruption-level” payload key:
{"aps":{"interruption-level":"time-sensitive"}}
新增 API
- UNNotificationInterruptionLevel 中斷級別枚舉。
- timeSensitiveSetting App 是否有時(shí)間敏感權(quán)限。
通知操作圖標(biāo)
通知操作現(xiàn)在可以包含圖標(biāo)以更好的表達(dá)相關(guān)操作。這些圖標(biāo)可以是 SFSymbol 系統(tǒng)圖像,也可以是 App 提供的自定義圖像。為了添加圖標(biāo),Apple 提供了新的 API UNNotificationActionIcon 對象。Action Icon 對象可以使用系統(tǒng)或模板圖像名稱進(jìn)行初始化,然后使用包含 icon 參數(shù)的新初始化方法添加到相應(yīng)的 UNNotificationAction 中。您不需要在名稱中指定文件擴(kuò)展名或大小修飾符,因?yàn)闀鶕?jù)系統(tǒng)和可用圖像資源自動檢索正確的大小。
通訊通知
Apple 添加了將應(yīng)用程序的通知區(qū)分為通信通知的功能。這些通知現(xiàn)在將包含發(fā)送它們的聯(lián)系人的圖像或頭像,并且可以與 SiriKit 集成,以便 Siri 可以根據(jù)常用聯(lián)系人智能地為通信操作提供快捷方式和建議。例如,當(dāng)用戶為焦點(diǎn)模式設(shè)置允許的聯(lián)系人或從您的應(yīng)用撥打電話時(shí)。 Siri 將根據(jù)您的應(yīng)用程序提供的意圖數(shù)據(jù)智能地推薦聯(lián)系人。
要使用通信通知,應(yīng)用程序需要在 Xcode 中向其應(yīng)用程序添加 Communication Notifications 功能,并使用實(shí)現(xiàn)了 UNNotificationContentProviding 協(xié)議的 Intent 對象更新 App Notification Service Extension 的通知內(nèi)容。目前兩個可用的實(shí)現(xiàn)是 INSendMessageIntent 和 INStartCallIntent。
APNs 實(shí)現(xiàn)通訊通知
項(xiàng)目中添加 Notification Service Extension,將以下 key value 添加到 Notification Service Extension 的 Info.plist 中。
在 NotificationService.didReceive 中編寫代碼向通知對象添加附加信息。(每次 Apple APNs 在通知用戶前,都會調(diào)用此方法)
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 裝載發(fā)送者信息,這些信息可以通過 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": "Neko@Neko.Neko",
"chat_session_id": "chat_1"
}
然后,可以通過使用 INPerson 和 INSendMessageIntent 將發(fā)送者信息添加到推送通知中。
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)
}
}
}