From 6611a28fa959891c6402aad6339f350df338e025 Mon Sep 17 00:00:00 2001 From: Matt Hayashida Date: Wed, 20 Mar 2024 14:17:44 -0400 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Determine=20push=20attachm?= =?UTF-8?q?ent=20file=20type=20using=20file=20mime=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppcuesKit/Push/ParsedNotification.swift | 2 - .../AppcuesNotificationServiceExtension.swift | 71 +++++++++++++------ 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/Sources/AppcuesKit/Push/ParsedNotification.swift b/Sources/AppcuesKit/Push/ParsedNotification.swift index 0debf2559..a26a4ad92 100644 --- a/Sources/AppcuesKit/Push/ParsedNotification.swift +++ b/Sources/AppcuesKit/Push/ParsedNotification.swift @@ -18,7 +18,6 @@ internal struct ParsedNotification { let deepLinkURL: URL? let experienceID: String? let attachmentURL: URL? - let attachmentType: String? let isTest: Bool let isInternal: Bool @@ -41,7 +40,6 @@ internal struct ParsedNotification { self.experienceID = userInfo["appcues_experience_id"] as? String self.attachmentURL = (userInfo["appcues_attachment_url"] as? String) .flatMap { URL(string: $0) } - self.attachmentType = userInfo["appcues_attachment_type"] as? String self.isTest = userInfo["appcues_test"] != nil self.isInternal = userInfo["_appcues_internal"] as? Bool ?? false } diff --git a/Sources/AppcuesNotificationService/AppcuesNotificationServiceExtension.swift b/Sources/AppcuesNotificationService/AppcuesNotificationServiceExtension.swift index 2a0726879..4f054d8cf 100644 --- a/Sources/AppcuesNotificationService/AppcuesNotificationServiceExtension.swift +++ b/Sources/AppcuesNotificationService/AppcuesNotificationServiceExtension.swift @@ -7,6 +7,8 @@ // import UserNotifications +import UniformTypeIdentifiers +import CoreServices /// `UNNotificationServiceExtension` subclass that implements Appcues functionality. /// @@ -28,11 +30,9 @@ open class AppcuesNotificationServiceExtension: UNNotificationServiceExtension { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - if let attachment = request.attachment { - bestAttemptContent?.attachments = [attachment] + if let bestAttemptContent = bestAttemptContent { + processAttachment(bestAttemptContent, contentHandler) } - - contentHandler(bestAttemptContent ?? request.content) } override public func serviceExtensionTimeWillExpire() { @@ -42,32 +42,57 @@ open class AppcuesNotificationServiceExtension: UNNotificationServiceExtension { contentHandler(bestAttemptContent) } } -} -extension UNNotificationRequest { - var attachment: UNNotificationAttachment? { + func processAttachment(_ content: UNMutableNotificationContent, _ contentHandler: @escaping (UNNotificationContent) -> Void) { guard let attachment = content.userInfo["appcues_attachment_url"] as? String, - let attachmentType = content.userInfo["appcues_attachment_type"] as? String, - let attachmentURL = URL(string: attachment), - let imageData = try? Data(contentsOf: attachmentURL) else { - return nil + let attachmentURL = URL(string: attachment) else { + contentHandler(content) + return } - return try? UNNotificationAttachment(data: imageData, dataType: attachmentType, options: nil) - } -} -extension UNNotificationAttachment { - convenience init(data: Data, dataType: String, options: [NSObject: AnyObject]?) throws { - let temporaryFolderName = ProcessInfo.processInfo.globallyUniqueString - let temporaryFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(temporaryFolderName, isDirectory: true) + let dataTask = URLSession.shared.downloadTask(with: attachmentURL) { url, response, error in + guard let downloadedURL = url, error == nil else { + contentHandler(content) + return + } + + let temporaryFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()) + let imageFileIdentifier = UUID().uuidString + let fileType = response?.fileType ?? "tmp" + + let tmpFileURL = temporaryFolderURL + .appendingPathComponent(imageFileIdentifier) + .appendingPathExtension(fileType) - try FileManager.default.createDirectory(at: temporaryFolderURL, withIntermediateDirectories: true, attributes: nil) + do { + try FileManager.default.moveItem(at: downloadedURL, to: tmpFileURL) + let attachment = try UNNotificationAttachment(identifier: imageFileIdentifier, url: tmpFileURL) + content.attachments = [attachment] + } catch { + print(error) + } - let imageFileIdentifier = UUID().uuidString + "." + dataType - let fileURL = temporaryFolderURL.appendingPathComponent(imageFileIdentifier) + contentHandler(content) + } + + dataTask.resume() + } +} - try data.write(to: fileURL) +extension URLResponse { + var fileType: String? { + guard let mimeType = self.mimeType else { + return self.url?.pathExtension + } - try self.init(identifier: imageFileIdentifier, url: fileURL, options: options) + if #available(iOS 14.0, *) { + return UTType(mimeType: mimeType)?.preferredFilenameExtension + } else { + guard let mimeUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeUnretainedValue(), + let extUTI = UTTypeCopyPreferredTagWithClass(mimeUTI, kUTTagClassFilenameExtension) + + else { return nil } + return extUTI.takeUnretainedValue() as String + } } }