✨ Add attachment onto iOS attachment
This commit is contained in:
parent
f5c06bc89c
commit
dc78f39969
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user