irpas技术客

通信通知 Communication Notifications 的实现 (iOS 15+)_iMazy_notifications 实现方法

网络 4069

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 #系统中对通知做了很多改变