带有 UIImage 或 Remote 的 UNNotificationAttachment URL
UNNotificationAttachment with UIImage or Remote URL
在我的 Notification Service Extension
中,我正在从 URL 下载图像以在通知中显示为 UNNotificationAttachment
。
所以我将这个图像作为 UIImage,并且没有看到需要将它写入我的应用程序目录/磁盘上的组容器只是为了设置通知。
有没有用 UIImage 创建 UNNotificationAttachment
的好方法?
(应该适用于本地和远程通知)
- 在 tmp 文件夹中创建目录
- 将
UIImage
的NSData
表示写入新创建的目录
- 使用 url 创建 UNNotificationAttachment 到 tmp 文件夹中的文件
- 清理 tmp 文件夹
我在 UINotificationAttachment
上写了一个扩展
extension UNNotificationAttachment {
static func create(identifier: String, image: UIImage, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL, withIntermediateDirectories: true, attributes: nil)
let imageFileIdentifier = identifier+".png"
let fileURL = tmpSubFolderURL.appendingPathComponent(imageFileIdentifier)
let imageData = UIImage.pngData(image)
try imageData()?.write(to: fileURL)
let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL, options: options)
return imageAttachment
} catch {
print("error " + error.localizedDescription)
}
return nil
}
}
因此,要从 UIImage
中使用 UNUserNotificationAttachment
创建 UNUserNotificationRequest
,您只需像这样
let identifier = ProcessInfo.processInfo.globallyUniqueString
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "World"
if let attachment = UNNotificationAttachment.create(identifier: identifier, image: myImage, options: nil) {
// where myImage is any UIImage
content.attachments = [attachment]
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120.0, repeats: false)
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
// handle error
}
这应该可行,因为 UNNotificationAttachment
会将图像文件复制到自己的位置。
我已经就此主题创建了一篇博文,主要关注 GIF 图像。但是为简单的图像重写我的代码应该很容易。
您需要创建通知服务扩展:
并包含此代码:
final class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ((UNNotificationContent) -> Void)?
private var bestAttemptContent: UNMutableNotificationContent?
override internal func didReceiveNotificationRequest(request: UNNotificationRequest, withContentHandler contentHandler: (UNNotificationContent) -> Void){
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
func failEarly() {
contentHandler(request.content)
}
guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
return failEarly()
}
guard let attachmentURL = content.userInfo["attachment-url"] as? String else {
return failEarly()
}
guard let imageData = NSData(contentsOfURL:NSURL(string: attachmentURL)!) else { return failEarly() }
guard let attachment = UNNotificationAttachment.create("image.gif", data: imageData, options: nil) else { return failEarly() }
content.attachments = [attachment]
contentHandler(content.copy() as! UNNotificationContent)
}
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 UNNotificationAttachment {
/// Save the image to disk
static func create(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = NSFileManager.defaultManager()
let tmpSubFolderName = NSProcessInfo.processInfo().globallyUniqueString
let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectoryAtURL(tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL?.URLByAppendingPathComponent(imageFileIdentifier)
try data.writeToURL(fileURL!, options: [])
let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, URL: fileURL!, options: options)
return imageAttachment
} catch let error {
print("error \(error)")
}
return nil
}
}
有关更多信息,您可以在此处查看我的博文:
http://www.avanderlee.com/ios-10/rich-notifications-ios-10/
从UIImage
看来这似乎是不可能的,我找到的所有解决方案都是下载图像并将其存储在本地某个地方。也有道理,因为您必须导入 UIKit
并且不确定它是否与扩展兼容(以及为什么在有更简单的解决方案时导入整个框架)。
Here 是一种更简单且可测试的解决方案,无需使用 FileManager。
这是一个完整的示例,说明如何实际从 Internet 下载图像并将其附加到本地通知(这是原始问题的一部分)。
let content = UNMutableNotificationContent()
content.title = "This is a test"
content.body = "Just checking the walls"
if let url = URL(string: "https://example.com/images/example.png") {
let pathExtension = url.pathExtension
let task = URLSession.shared.downloadTask(with: url) { (result, response, error) in
if let result = result {
let identifier = ProcessInfo.processInfo.globallyUniqueString
let target = FileManager.default.temporaryDirectory.appendingPathComponent(identifier).appendingPathExtension(pathExtension)
do {
try FileManager.default.moveItem(at: result, to: target)
let attachment = try UNNotificationAttachment(identifier: identifier, url: target, options: nil)
content.attachments.append(attachment)
let notification = UNNotificationRequest(identifier: Date().description, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notification, withCompletionHandler: { (error) in
if let error = error {
print(error.localizedDescription)
}
})
}
catch {
print(error.localizedDescription)
}
}
}
task.resume()
}
通常情况下,下载的文件已经是有效图片时,不需要重新制作图片。只需将下载的文件复制到具有唯一名称和 .png
或 .jpg
扩展名的当前 Temp 目录。也没有必要在现有的 Temp 目录中创建子目录。
在我的 Notification Service Extension
中,我正在从 URL 下载图像以在通知中显示为 UNNotificationAttachment
。
所以我将这个图像作为 UIImage,并且没有看到需要将它写入我的应用程序目录/磁盘上的组容器只是为了设置通知。
有没有用 UIImage 创建 UNNotificationAttachment
的好方法?
(应该适用于本地和远程通知)
- 在 tmp 文件夹中创建目录
- 将
UIImage
的NSData
表示写入新创建的目录 - 使用 url 创建 UNNotificationAttachment 到 tmp 文件夹中的文件
- 清理 tmp 文件夹
我在 UINotificationAttachment
extension UNNotificationAttachment {
static func create(identifier: String, image: UIImage, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL, withIntermediateDirectories: true, attributes: nil)
let imageFileIdentifier = identifier+".png"
let fileURL = tmpSubFolderURL.appendingPathComponent(imageFileIdentifier)
let imageData = UIImage.pngData(image)
try imageData()?.write(to: fileURL)
let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL, options: options)
return imageAttachment
} catch {
print("error " + error.localizedDescription)
}
return nil
}
}
因此,要从 UIImage
中使用 UNUserNotificationAttachment
创建 UNUserNotificationRequest
,您只需像这样
let identifier = ProcessInfo.processInfo.globallyUniqueString
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "World"
if let attachment = UNNotificationAttachment.create(identifier: identifier, image: myImage, options: nil) {
// where myImage is any UIImage
content.attachments = [attachment]
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120.0, repeats: false)
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
// handle error
}
这应该可行,因为 UNNotificationAttachment
会将图像文件复制到自己的位置。
我已经就此主题创建了一篇博文,主要关注 GIF 图像。但是为简单的图像重写我的代码应该很容易。
您需要创建通知服务扩展:
并包含此代码:
final class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ((UNNotificationContent) -> Void)?
private var bestAttemptContent: UNMutableNotificationContent?
override internal func didReceiveNotificationRequest(request: UNNotificationRequest, withContentHandler contentHandler: (UNNotificationContent) -> Void){
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
func failEarly() {
contentHandler(request.content)
}
guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
return failEarly()
}
guard let attachmentURL = content.userInfo["attachment-url"] as? String else {
return failEarly()
}
guard let imageData = NSData(contentsOfURL:NSURL(string: attachmentURL)!) else { return failEarly() }
guard let attachment = UNNotificationAttachment.create("image.gif", data: imageData, options: nil) else { return failEarly() }
content.attachments = [attachment]
contentHandler(content.copy() as! UNNotificationContent)
}
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 UNNotificationAttachment {
/// Save the image to disk
static func create(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = NSFileManager.defaultManager()
let tmpSubFolderName = NSProcessInfo.processInfo().globallyUniqueString
let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectoryAtURL(tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL?.URLByAppendingPathComponent(imageFileIdentifier)
try data.writeToURL(fileURL!, options: [])
let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, URL: fileURL!, options: options)
return imageAttachment
} catch let error {
print("error \(error)")
}
return nil
}
}
有关更多信息,您可以在此处查看我的博文: http://www.avanderlee.com/ios-10/rich-notifications-ios-10/
从UIImage
看来这似乎是不可能的,我找到的所有解决方案都是下载图像并将其存储在本地某个地方。也有道理,因为您必须导入 UIKit
并且不确定它是否与扩展兼容(以及为什么在有更简单的解决方案时导入整个框架)。
Here 是一种更简单且可测试的解决方案,无需使用 FileManager。
这是一个完整的示例,说明如何实际从 Internet 下载图像并将其附加到本地通知(这是原始问题的一部分)。
let content = UNMutableNotificationContent()
content.title = "This is a test"
content.body = "Just checking the walls"
if let url = URL(string: "https://example.com/images/example.png") {
let pathExtension = url.pathExtension
let task = URLSession.shared.downloadTask(with: url) { (result, response, error) in
if let result = result {
let identifier = ProcessInfo.processInfo.globallyUniqueString
let target = FileManager.default.temporaryDirectory.appendingPathComponent(identifier).appendingPathExtension(pathExtension)
do {
try FileManager.default.moveItem(at: result, to: target)
let attachment = try UNNotificationAttachment(identifier: identifier, url: target, options: nil)
content.attachments.append(attachment)
let notification = UNNotificationRequest(identifier: Date().description, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notification, withCompletionHandler: { (error) in
if let error = error {
print(error.localizedDescription)
}
})
}
catch {
print(error.localizedDescription)
}
}
}
task.resume()
}
通常情况下,下载的文件已经是有效图片时,不需要重新制作图片。只需将下载的文件复制到具有唯一名称和 .png
或 .jpg
扩展名的当前 Temp 目录。也没有必要在现有的 Temp 目录中创建子目录。