WWDC 2021 苹果在 iOS 15 系统中对通知做了很多改变, 让通知更加个性化.
这里只有讨论通信通知 Communication Notifications, 苹果自带的很多应用, 以及第三方App 飞书, 都使用了这个通知功能。
通信通知 Communication Notifications 简介iOS 15系统后, Apple 添加了通信通知的功能。这些通知将包含发送它们的联系人的头像,并且可以与 SiriKit 集成,以便 Siri 可以智能地根据常用联系人提供通信操作的快捷方式和建议。
图例:
通信通 Communication Notifications 具体实现:
要使用通信通知,App 需要在 Xcode 中将通信通知功能添加到其应用程序,并在应用程序通知服务扩展中实现 UNNotificationContentProviding 协议。
1.首先将以下键值添加到主应用程序 Info.plist 文件中 NSUserActivityTypes (Array) - INStartCallIntent - INSendMessageIntent具体位置如图所示:
2.在 Xcode -> Capabilities 中添加 Communication Notifications 功能如图所示:
3.添加 Notification Service Extension 扩展大多数社交媒体通知都是从服务器发送到 Apple 的 APN 服务器,然后再发送到设备。 我们需要使用通知服务扩展, 该扩展用于处理通知,然后将它们显示在屏幕上。
首先,将扩展Notification Service Extension 添加到项目中
然后将以下键和值添加到 Notification Service Extension 扩展 Info.plist 中
4.在 Notification Service Extension 扩展下 NotificationService 文件中, 重写 didReceive 方法Apple APN 服务器每次在通知出现在用户屏幕上之前,都会调用此方法
初始代码如下:
import UIKit import Intents import UserNotifications 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或INSendMessageIntent创建此信息将其添加到您的推送通知消息中
具体实现代码如下:
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... // 获取通信消息 if let senderAccountID = bestAttemptContent.userInfo["sender_id"] as? String, // 发送者名称 let senderName = bestAttemptContent.userInfo["sender_name"] as? String, // 发送者图像url地址 let senderImageURLString = bestAttemptContent.userInfo["sender_image_url"] as? String, // 发送者昵称 let senderDisplayName = bestAttemptContent.userInfo["sender_nickname"] as? String, // 通信id let chatSessionID = bestAttemptContent.userInfo["chat-session_id"] as? String { // Here you need to download the image data from the URL. self.getMediaAttachment(for: senderImageURLString) { image in guard let groupIcon = image else { contentHandler(bestAttemptContent) return } let avatar = INImage(imageData: groupIcon.pngData()!) // 消息发送方 let messageSender = INPerson( personHandle: INPersonHandle(value: nil, type: .unknown), nameComponents: try? PersonNameComponents(senderName), displayName: senderDisplayName, image: avatar, contactIdentifier: nil, customIdentifier: senderAccountID, isMe: false, suggestionType: .none ) // 消息接收方 let mePerson = INPerson( personHandle: INPersonHandle(value: "", type: .unknown), nameComponents: nil, displayName: nil, image: nil, contactIdentifier: nil, customIdentifier: nil, isMe: true, suggestionType: .none ) let intent = INSendMessageIntent(recipients: [mePerson, messageSender], outgoingMessageType: .outgoingMessageText, content: bestAttemptContent.body, speakableGroupName: INSpeakableString(spokenPhrase: senderDisplayName), conversationIdentifier: chatSessionID, serviceName: nil, sender: messageSender, attachments: nil) intent.setImage(avatar, forParameterNamed: \.speakableGroupName) 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) } } } 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) } } }使用到的工具扩展, 包含图片下载和本地保存
extension NotificationService { // 保存图片到本地 并返回 本地 url 地址 private func saveImageAttachment(image: UIImage, forIdentifier identifier: String) -> URL? { // 1 获取临时文件夹 let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()) // 2 拼接文件路径 let directoryPath = tempDirectory.appendingPathComponent( ProcessInfo.processInfo.globallyUniqueString, isDirectory: true) do { // 3 如果文件夹不存在 创建文件夹 try FileManager.default.createDirectory( at: directoryPath, withIntermediateDirectories: true, attributes: nil) // 4 文件地址URL let fileURL = directoryPath.appendingPathComponent(identifier) // 5 文件二进制 guard let imageData = image.pngData() else { return nil } // 6 保存二进制文档到本地路径 try imageData.write(to: fileURL) return fileURL } catch { return nil } } // 通过本地 url 地址 获取图片资源 private func getMediaAttachment(for urlString: String, completion: @escaping (UIImage?) -> Void) { // 1 guard let url = URL(string: urlString) else { completion(nil) return } // 2 通过远程图片URL下载图片 downloadImage(forURL: url) { result in // 3 guard let image = try? result.get() else { completion(nil) return } // 4 completion(image) } } // 通过远程图片url地址 下载图片文件 public enum DownloadError: Error { case emptyData case invalidImage } private func downloadImage(forURL url: URL, completion: @escaping (Result<UIImage, Error>) -> Void) { let task = URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { completion(.failure(error)) return } guard let data = data else { completion(.failure(DownloadError.emptyData)) return } guard let image = UIImage(data: data) else { completion(.failure(DownloadError.invalidImage)) return } completion(.success(image)) } task.resume() } }额外补充: 通知 Notifications 实现附件图片展示 在回调 bestAttemptContent 之前, 给它设置 attachments属性即可 @available(iOS 10.0, *) open class UNMutableNotificationContent : UNNotificationContent { /// ... // Optional array of attachments. open var attachments: [UNNotificationAttachment] /// ... }
核心代码如下:
// 创建图片附件 let imageAttachment = try? UNNotificationAttachment( identifier: "image", url: "图片本地路径, 和上面设置 Avatar 地址一样", options: nil) // 赋值给 bestAttemptContent if let imageAttachment = imageAttachment { bestAttemptContent.attachments = [imageAttachment] } // 回调出去 contentHandler(bestAttemptContent)
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。 |
标签: #Notifications #实现方法 #WWDC #2021 #苹果在 #iOS #15 #系统中对通知做了很多改变