Add attachment onto iOS attachment

This commit is contained in:
LittleSheep 2024-12-21 16:56:55 +08:00
parent f5c06bc89c
commit dc78f39969
2 changed files with 53 additions and 38 deletions

View File

@ -8,6 +8,7 @@
import UserNotifications import UserNotifications
import Intents import Intents
import Kingfisher import Kingfisher
import UniformTypeIdentifiers
enum ParseNotificationPayloadError: Error { enum ParseNotificationPayloadError: Error {
case missingMetadata(String) case missingMetadata(String)
@ -102,15 +103,15 @@ class NotificationService: UNNotificationServiceExtension {
} }
if let imageIdentifier = metadata["image"] as? String { if let imageIdentifier = metadata["image"] as? String {
attachMedia(to: content, withIdentifier: imageIdentifier) attachMedia(to: content, withIdentifier: imageIdentifier, fileType: UTType.jpeg, doScaleDown: true)
} else if let avatarIdentifier = metadata["avatar"] as? String { } else if let avatarIdentifier = metadata["avatar"] as? String {
attachMedia(to: content, withIdentifier: avatarIdentifier) attachMedia(to: content, withIdentifier: avatarIdentifier, fileType: UTType.jpeg, doScaleDown: true)
} else {
contentHandler?(content)
} }
contentHandler?(content)
} }
private func attachMedia(to content: UNMutableNotificationContent, withIdentifier identifier: String) { private func attachMedia(to content: UNMutableNotificationContent, withIdentifier identifier: String, fileType type: UTType?, doScaleDown scaleDown: Bool = false) {
let attachmentUrl = getAttachmentUrl(for: identifier) let attachmentUrl = getAttachmentUrl(for: identifier)
guard let remoteUrl = URL(string: attachmentUrl) else { guard let remoteUrl = URL(string: attachmentUrl) else {
@ -118,49 +119,62 @@ class NotificationService: UNNotificationServiceExtension {
return return
} }
// Define a cache location based on the identifier let targetSize = 800
let tempDirectory = FileManager.default.temporaryDirectory let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: targetSize, height: targetSize), mode: .aspectFit)
let cachedFileUrl = tempDirectory.appendingPathComponent(identifier)
if FileManager.default.fileExists(atPath: cachedFileUrl.path) { KingfisherManager.shared.retrieveImage(with: remoteUrl, options: scaleDown ? [
// Use cached file .processor(scaleProcessor)
attachLocalMedia(to: content, from: cachedFileUrl, withIdentifier: identifier) ] : nil) { [weak self] result in
} else { guard let self = self else { return }
// Download and cache the file
let session = URLSession(configuration: .default) switch result {
session.downloadTask(with: remoteUrl) { [weak content] localUrl, response, error in case .success(let retrievalResult):
guard let content = content else { return } // The image is either retrieved from cache or downloaded
let tempDirectory = FileManager.default.temporaryDirectory
if let error = error { let cachedFileUrl = tempDirectory.appendingPathComponent(identifier)
print("Failed to download media: \(error.localizedDescription)")
self.contentHandler?(content)
return
}
guard let localUrl = localUrl else {
print("No local file URL after download")
self.contentHandler?(content)
return
}
do { do {
// Move the downloaded file to the cache // Write the image data to a temporary file for UNNotificationAttachment
try FileManager.default.moveItem(at: localUrl, to: cachedFileUrl) try retrievalResult.image.pngData()?.write(to: cachedFileUrl)
self.attachLocalMedia(to: content, from: cachedFileUrl, withIdentifier: identifier) self.attachLocalMedia(to: content, fileType: type?.identifier, from: cachedFileUrl, withIdentifier: identifier)
} catch { } catch {
print("Failed to cache media file: \(error.localizedDescription)") print("Failed to write media to temporary file: \(error.localizedDescription)")
self.contentHandler?(content) self.contentHandler?(content)
} }
}.resume()
case .failure(let error):
print("Failed to retrieve image: \(error.localizedDescription)")
self.contentHandler?(content)
}
} }
} }
private func attachLocalMedia(to content: UNMutableNotificationContent, from localUrl: URL, withIdentifier identifier: String) { private func attachLocalMedia(to content: UNMutableNotificationContent, fileType type: String?, from localUrl: URL, withIdentifier identifier: String) {
if let attachment = try? UNNotificationAttachment(identifier: identifier, url: localUrl) { do {
let attachment = try UNNotificationAttachment(identifier: identifier, url: localUrl, options: [
UNNotificationAttachmentOptionsTypeHintKey: type as Any,
UNNotificationAttachmentOptionsThumbnailHiddenKey: 0,
])
content.attachments = [attachment] content.attachments = [attachment]
} else { } catch let error as NSError {
print("Failed to create attachment from cached file: \(localUrl.path)") // Log detailed error information
print("Failed to create attachment from file at \(localUrl.path)")
print("Error: \(error.localizedDescription)")
// Check specific error codes if needed
if error.domain == NSCocoaErrorDomain {
switch error.code {
case NSFileReadNoSuchFileError:
print("File does not exist at \(localUrl.path)")
case NSFileReadNoPermissionError:
print("No permission to read file at \(localUrl.path)")
default:
print("Unhandled file error: \(error.code)")
}
}
} }
// Call content handler regardless of success or failure
self.contentHandler?(content) self.contentHandler?(content)
} }

View File

@ -73,10 +73,11 @@ struct RandomPostWidgetEntryView : View {
if let avatar = randomPost.publisher.avatar { if let avatar = randomPost.publisher.avatar {
let avatarUrl = getAttachmentUrl(for: avatar) let avatarUrl = getAttachmentUrl(for: avatar)
let size: CGFloat = 28 let size: CGFloat = 28
let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: size, height: size), mode: .aspectFit)
KFImage.url(URL(string: avatarUrl)) KFImage.url(URL(string: avatarUrl))
.resizable() .resizable()
.setProcessor(DownsamplingImageProcessor(size: CGSize(width: size, height: size))) .setProcessor(scaleProcessor)
.transition(.opacity) .transition(.opacity)
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: size, height: size) .frame(width: size, height: size)