diff --git a/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift b/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift index b7123b5653..23df7ce57f 100644 --- a/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift +++ b/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift @@ -7,6 +7,7 @@ import CoreServices import Photos import SignalServiceKit import SignalUI +import UniformTypeIdentifiers extension ConversationViewController: ConversationInputToolbarDelegate { @@ -729,19 +730,17 @@ extension ConversationViewController: UIDocumentPickerDelegate { public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { Logger.debug("Picked document at url: \(url)") - let typeIdentifier: String = { + let contentType: UTType = { do { - let resourceValues = try url.resourceValues(forKeys: Set([ - .typeIdentifierKey - ])) - guard let typeIdentifier = resourceValues.typeIdentifier else { - owsFailDebug("Missing typeIdentifier.") - return kUTTypeData as String + let resourceValues = try url.resourceValues(forKeys: [.contentTypeKey]) + guard let contentType = resourceValues.contentType else { + owsFailDebug("Missing contentType.") + return .data } - return typeIdentifier + return contentType } catch { owsFailDebug("Error: \(error)") - return kUTTypeData as String + return .data } }() let isDirectory: Bool = { @@ -802,12 +801,12 @@ extension ConversationViewController: UIDocumentPickerDelegate { // Although we want to be able to send higher quality attachments through the document picker // it's more important that we ensure the sent format is one all clients can accept (e.g. *not* quicktime .mov) if SignalAttachment.isVideoThatNeedsCompression(dataSource: dataSource, - dataUTI: typeIdentifier) { + dataUTI: contentType.identifier) { self.showApprovalDialogAfterProcessingVideoURL(url, filename: filename) return } - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: typeIdentifier) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: contentType.identifier) showApprovalDialog(forAttachment: attachment) } @@ -831,7 +830,7 @@ extension ConversationViewController: UIDocumentPickerDelegate { dataSource.sourceFilename = filename let promise = Promise.wrapAsync({ return try await SignalAttachment.compressVideoAsMp4(dataSource: dataSource, - dataUTI: kUTTypeMPEG4 as String) + dataUTI: UTType.mpeg4Movie.identifier) }) promise.done(on: DispatchQueue.main) { (attachment: SignalAttachment) in if modalActivityIndicator.wasCancelled { diff --git a/Signal/ConversationView/VoiceMessage/VoiceMessageSendableDraft.swift b/Signal/ConversationView/VoiceMessage/VoiceMessageSendableDraft.swift index 4677439574..0663c31eeb 100644 --- a/Signal/ConversationView/VoiceMessage/VoiceMessageSendableDraft.swift +++ b/Signal/ConversationView/VoiceMessage/VoiceMessageSendableDraft.swift @@ -6,6 +6,7 @@ import CoreServices import Foundation import SignalServiceKit +import UniformTypeIdentifiers protocol VoiceMessageSendableDraft { func prepareForSending() throws -> URL @@ -27,7 +28,7 @@ extension VoiceMessageSendableDraft { let dataSource = try DataSourcePath.dataSource(with: attachmentUrl, shouldDeleteOnDeallocation: true) dataSource.sourceFilename = userVisibleFilename(currentDate: Date()) - let attachment = SignalAttachment.voiceMessageAttachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4Audio as String) + let attachment = SignalAttachment.voiceMessageAttachment(dataSource: dataSource, dataUTI: UTType.mpeg4Audio.identifier) guard !attachment.hasError else { throw OWSAssertionError("Failed to create voice message attachment: \(attachment.errorName ?? "Unknown Error")") } diff --git a/Signal/src/ViewControllers/Avatars/AvatarSettingsViewController.swift b/Signal/src/ViewControllers/Avatars/AvatarSettingsViewController.swift index 8a7d50c4e7..5dec1034a1 100644 --- a/Signal/src/ViewControllers/Avatars/AvatarSettingsViewController.swift +++ b/Signal/src/ViewControllers/Avatars/AvatarSettingsViewController.swift @@ -6,6 +6,7 @@ import CoreServices import SignalServiceKit import SignalUI +import UniformTypeIdentifiers class AvatarSettingsViewController: OWSTableViewController2 { let context: AvatarContext @@ -334,7 +335,7 @@ class AvatarSettingsViewController: OWSTableViewController2 { picker.delegate = self picker.allowsEditing = false picker.sourceType = .camera - picker.mediaTypes = [kUTTypeImage as String] + picker.mediaTypes = [UTType.image.identifier] self.present(picker, animated: true) } } @@ -352,7 +353,7 @@ class AvatarSettingsViewController: OWSTableViewController2 { let picker = OWSImagePickerController() picker.delegate = self picker.sourceType = .photoLibrary - picker.mediaTypes = [kUTTypeImage as String] + picker.mediaTypes = [UTType.image.identifier] self.present(picker, animated: true) } } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.swift b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.swift index 4d910af736..d7aef6acce 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.swift +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.swift @@ -7,6 +7,7 @@ import CoreServices import LibSignalClient import SignalServiceKit import SignalUI +import UniformTypeIdentifiers #if USE_DEBUG_UI @@ -101,7 +102,7 @@ class DebugUIMessages: DebugUIPage, Dependencies { DebugUIMessages.sendRandomAttachmentInThread(thread, uti: MimeTypeUtil.unknownTestAttachmentUti) }), OWSTableItem(title: "Send pdf", actionBlock: { - DebugUIMessages.sendRandomAttachmentInThread(thread, uti: kUTTypePDF as String) + DebugUIMessages.sendRandomAttachmentInThread(thread, uti: UTType.pdf.identifier) }), OWSTableItem(title: "Create all system messages", actionBlock: { DebugUIMessages.createSystemMessagesInThread(thread) @@ -1739,12 +1740,12 @@ class DebugUIMessages: DebugUIPage, Dependencies { sendUnsafeFile = { guard let filename = filenames.popLast() else { return } - let utiType = kUTTypeData as String + let type = UTType.data let dataLength: UInt32 = 32 - guard let dataSource = DataSourceValue.dataSource(with: createRandomDataOfSize(dataLength), utiType: utiType) else { return } + guard let dataSource = DataSourceValue.dataSource(with: createRandomDataOfSize(dataLength), utiType: type.identifier) else { return } dataSource.sourceFilename = filename - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: utiType) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: type.identifier) guard attachment.hasError else { Logger.error("attachment[\(String(describing: attachment.sourceFilename))]: \(String(describing: attachment.errorName))") diff --git a/Signal/src/ViewControllers/LocationPicker.swift b/Signal/src/ViewControllers/LocationPicker.swift index 8c8bb9e61b..e77393d197 100644 --- a/Signal/src/ViewControllers/LocationPicker.swift +++ b/Signal/src/ViewControllers/LocationPicker.swift @@ -14,6 +14,7 @@ import CoreServices import MapKit import SignalServiceKit import SignalUI +import UniformTypeIdentifiers public protocol LocationPickerDelegate: AnyObject { func didPickLocation(_ locationPicker: LocationPicker, location: Location) @@ -496,8 +497,8 @@ public class Location: NSObject { throw LocationError.assertion } - let dataSource = DataSourceValue.dataSource(with: jpegData, utiType: kUTTypeJPEG as String) - return SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeJPEG as String) + let dataSource = DataSourceValue.dataSource(with: jpegData, utiType: UTType.jpeg.identifier) + return SignalAttachment.attachment(dataSource: dataSource, dataUTI: UTType.jpeg.identifier) } } diff --git a/Signal/src/ViewControllers/Photos/CameraCaptureSession.swift b/Signal/src/ViewControllers/Photos/CameraCaptureSession.swift index d03f577801..cc4cc1a293 100644 --- a/Signal/src/ViewControllers/Photos/CameraCaptureSession.swift +++ b/Signal/src/ViewControllers/Photos/CameraCaptureSession.swift @@ -844,7 +844,7 @@ class CameraCaptureSession: NSObject { return handleVideoCaptureError(PhotoCaptureError.captureFailed) } - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4 as String) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: UTType.mpeg4Movie.identifier) delegate.cameraCaptureSession(self, didFinishProcessing: attachment) } @@ -1010,9 +1010,9 @@ extension CameraCaptureSession: PhotoCaptureDelegate { case .failure(let error): delegate.cameraCaptureSession(self, didFailWith: error) case .success(let photoData): - let dataSource = DataSourceValue.dataSource(with: photoData, utiType: kUTTypeJPEG as String) + let dataSource = DataSourceValue.dataSource(with: photoData, utiType: UTType.jpeg.identifier) - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeJPEG as String) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: UTType.jpeg.identifier) delegate.cameraCaptureSession(self, didFinishProcessing: attachment) } } diff --git a/Signal/src/ViewControllers/Photos/PhotoLibrary.swift b/Signal/src/ViewControllers/Photos/PhotoLibrary.swift index 30f5cc353f..de99bca7b1 100644 --- a/Signal/src/ViewControllers/Photos/PhotoLibrary.swift +++ b/Signal/src/ViewControllers/Photos/PhotoLibrary.swift @@ -193,7 +193,7 @@ class PhotoAlbumContents { let baseFilename: String? if let onDiskVideo = video as? AVURLAsset { let url = onDiskVideo.url - dataUTI = MimeTypeUtil.utiTypeForFileExtension(url.pathExtension) ?? kUTTypeVideo as String + dataUTI = MimeTypeUtil.utiTypeForFileExtension(url.pathExtension) ?? UTType.video.identifier if let dataSource = try? DataSourcePath.dataSource(with: url, shouldDeleteOnDeallocation: false) { if !SignalAttachment.isVideoThatNeedsCompression(dataSource: dataSource, dataUTI: dataUTI) { @@ -204,7 +204,7 @@ class PhotoAlbumContents { baseFilename = url.lastPathComponent } else { - dataUTI = kUTTypeVideo as String + dataUTI = UTType.video.identifier baseFilename = nil } diff --git a/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift b/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift index f754b5d946..12bfc0ddbe 100644 --- a/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift +++ b/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift @@ -6,6 +6,7 @@ import CoreServices import SignalServiceKit import SignalUI +import UniformTypeIdentifiers class SetWallpaperViewController: OWSTableViewController2 { lazy var collectionView = WallpaperCollectionView(container: self, shouldDimInDarkMode: shouldDimInDarkMode) { [weak self] wallpaper in @@ -84,7 +85,7 @@ class SetWallpaperViewController: OWSTableViewController2 { let vc = UIImagePickerController() vc.delegate = self vc.sourceType = .photoLibrary - vc.mediaTypes = [kUTTypeImage as String] + vc.mediaTypes = [UTType.image.identifier] self.presentFormSheet(vc, animated: true) } photosSection.add(choosePhotoItem) diff --git a/Signal/test/attachments/SignalAttachmentTest.swift b/Signal/test/attachments/SignalAttachmentTest.swift index 0c0da39883..f097dd17f6 100644 --- a/Signal/test/attachments/SignalAttachmentTest.swift +++ b/Signal/test/attachments/SignalAttachmentTest.swift @@ -6,6 +6,7 @@ import XCTest import CoreServices import SignalServiceKit +import UniformTypeIdentifiers class SignalAttachmentTest: SignalBaseTest { // MARK: - Utilities @@ -16,7 +17,7 @@ class SignalAttachmentTest: SignalBaseTest { let dataSource = try DataSourcePath.dataSource(with: url, shouldDeleteOnDeallocation: false) let attachment = SignalAttachment.attachment( dataSource: dataSource, - dataUTI: kUTTypeJPEG as String + dataUTI: UTType.jpeg.identifier ) let newSize = attachment.data.imageMetadata(withPath: nil, mimeType: "image/jpeg").pixelSize @@ -60,7 +61,7 @@ class SignalAttachmentTest: SignalBaseTest { let attachment = SignalAttachment.attachment( dataSource: dataSource, - dataUTI: kUTTypePNG as String + dataUTI: UTType.png.identifier ) XCTAssertEqual( @@ -98,7 +99,7 @@ class SignalAttachmentTest: SignalBaseTest { let attachment = SignalAttachment.attachment( dataSource: dataSource, - dataUTI: kUTTypePNG as String + dataUTI: UTType.png.identifier ) XCTAssert( diff --git a/SignalServiceKit/Attachments/SignalAttachment.swift b/SignalServiceKit/Attachments/SignalAttachment.swift index 7f4086631f..e438963460 100644 --- a/SignalServiceKit/Attachments/SignalAttachment.swift +++ b/SignalServiceKit/Attachments/SignalAttachment.swift @@ -433,10 +433,7 @@ public class SignalAttachment: NSObject { if dataUTI == MimeTypeUtil.unknownTestAttachmentUti { return MimeType.unknownMimetype.rawValue } - guard let mimeType = UTTypeCopyPreferredTagWithClass(dataUTI as CFString, kUTTagClassMIMEType) else { - return MimeType.applicationOctetStream.rawValue - } - return mimeType.takeRetainedValue() as String + return UTType(dataUTI)?.preferredMIMEType ?? MimeType.applicationOctetStream.rawValue } // Use the filename if known. If not, e.g. if the attachment was copy/pasted, we'll generate a filename @@ -502,7 +499,7 @@ public class SignalAttachment: NSObject { } private class var outputVideoUTISet: Set { - return Set([kUTTypeMPEG4 as String]) + [UTType.mpeg4Movie.identifier] } // Returns the set of UTIs that correspond to valid animated image formats @@ -557,11 +554,12 @@ public class SignalAttachment: NSObject { } public var isText: Bool { - return UTTypeConformsTo(dataUTI as CFString, kUTTypeText) || isOversizeText + let isText = UTType(dataUTI)?.conforms(to: .text) ?? false + return isText || isOversizeText } public var isUrl: Bool { - return UTTypeConformsTo(dataUTI as CFString, kUTTypeURL) + UTType(dataUTI)?.conforms(to: .url) ?? false } public class func pasteboardHasPossibleAttachment() -> Bool { @@ -608,13 +606,13 @@ public class SignalAttachment: NSObject { var hasTextUTIType = false var hasNonTextUTIType = false for utiType in pasteboardUTISet { - if UTTypeConformsTo(utiType as CFString, kUTTypeText) { + if let type = UTType(utiType), type.conforms(to: .text) { hasTextUTIType = true } else if mediaUTISet.contains(utiType) { hasNonTextUTIType = true } } - if pasteboardUTISet.contains(kUTTypeURL as String) { + if pasteboardUTISet.contains(UTType.url.identifier) { // Treat URL as a textual UTI type. hasTextUTIType = true } @@ -659,8 +657,8 @@ public class SignalAttachment: NSObject { // and png uti types when sending memoji stickers and // `inputImageUTISet` is unordered, so without this check there // is a 50/50 chance that we'd pick the jpg. - if pasteboardUTISet.isSuperset(of: [kUTTypeJPEG as String, kUTTypePNG as String]) { - pasteboardUTISet.remove(kUTTypeJPEG as String) + if pasteboardUTISet.isSuperset(of: [UTType.jpeg.identifier, UTType.png.identifier]) { + pasteboardUTISet.remove(UTType.jpeg.identifier) } for dataUTI in inputImageUTISet { @@ -762,7 +760,7 @@ public class SignalAttachment: NSObject { } // Never re-encode animated images (i.e. GIFs) as JPEGs. - if dataUTI == (kUTTypePNG as String) { + if dataUTI == UTType.png.identifier { do { return try attachment.removingImageMetadata() } catch { @@ -777,7 +775,7 @@ public class SignalAttachment: NSObject { if let sourceFilename = dataSource.sourceFilename, let sourceFileExtension = sourceFilename.fileExtension, ["heic", "heif"].contains(sourceFileExtension.lowercased()), - dataUTI == kUTTypeJPEG as String { + dataUTI == UTType.jpeg.identifier as String { // If a .heic file actually contains jpeg data, update the extension to match. // @@ -822,7 +820,7 @@ public class SignalAttachment: NSObject { // JPEGs and instead go through the recompresing step. // This is an iOS bug (FB13285956) still present in iOS 17 and should // be revisitied in the future to see if JPEG support can be reenabled. - guard dataUTI != (kUTTypeJPEG as String) else { return false } + guard dataUTI != UTType.jpeg.identifier else { return false } guard SignalAttachment.outputImageUTISet.contains(dataUTI) else { return false } guard dataSource.dataLength <= imageQuality.maxFileSize else { return false } @@ -909,7 +907,7 @@ public class SignalAttachment: NSObject { // so we can keep the image out of memory. let dataFileExtension: String - let dataUTI: CFString + let dataType: UTType // We convert everything that's not sticker-like to jpg, because // often images with alpha channels don't actually have any @@ -918,15 +916,15 @@ public class SignalAttachment: NSObject { // are any transparent pixels in an image. if dataSource.hasStickerLikeProperties { dataFileExtension = "png" - dataUTI = kUTTypePNG + dataType = .png } else { dataFileExtension = "jpg" - dataUTI = kUTTypeJPEG + dataType = .jpeg imageProperties[kCGImageDestinationLossyCompressionQuality] = compressionQuality(for: pixelSize) } let tempFileUrl = OWSFileSystem.temporaryFileUrl(fileExtension: dataFileExtension) - guard let destination = CGImageDestinationCreateWithURL(tempFileUrl as CFURL, dataUTI, 1, nil) else { + guard let destination = CGImageDestinationCreateWithURL(tempFileUrl as CFURL, dataType.identifier as CFString, 1, nil) else { owsFailDebug("Failed to create CGImageDestination for attachment") return .error(error: .couldNotConvertImage) } @@ -950,7 +948,7 @@ public class SignalAttachment: NSObject { outputDataSource.sourceFilename = newFilenameWithExtension if outputDataSource.dataLength <= imageQuality.maxFileSize, outputDataSource.dataLength <= kMaxFileSizeImage { - let recompressedAttachment = attachment.replacingDataSource(with: outputDataSource, dataUTI: dataUTI as String) + let recompressedAttachment = attachment.replacingDataSource(with: outputDataSource, dataUTI: dataType.identifier) return .signalAttachment(signalAttachment: recompressedAttachment) } @@ -1065,7 +1063,7 @@ public class SignalAttachment: NSObject { private func removingImageMetadata() throws -> SignalAttachment { owsAssertDebug(isImage) - if dataUTI == (kUTTypePNG as String) { + if dataUTI == UTType.png.identifier { let cleanedData = try Self.removeMetadata(fromPng: data) guard let dataSource = DataSourceValue.dataSource(with: cleanedData, utiType: dataUTI) else { throw SignalAttachmentError.couldNotRemoveMetadata @@ -1234,7 +1232,7 @@ public class SignalAttachment: NSObject { shouldDeleteOnDeallocation: true) dataSource.sourceFilename = mp4Filename - return SignalAttachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4 as String) + return SignalAttachment(dataSource: dataSource, dataUTI: UTType.mpeg4Movie.identifier) } catch { owsFailDebug("Failed to build data source for exported video URL") let attachment = SignalAttachment(dataSource: DataSourceValue.emptyDataSource(), dataUTI: dataUTI) @@ -1323,8 +1321,7 @@ public class SignalAttachment: NSObject { } public class func empty() -> SignalAttachment { - return SignalAttachment.attachment(dataSource: DataSourceValue.emptyDataSource(), - dataUTI: kUTTypeContent as String) + SignalAttachment.attachment(dataSource: DataSourceValue.emptyDataSource(), dataUTI: UTType.content.identifier) } // MARK: Helper Methods diff --git a/SignalServiceKit/Network/API/Giphy/GiphyAsset.swift b/SignalServiceKit/Network/API/Giphy/GiphyAsset.swift index 7210b94579..2fc5aa643a 100644 --- a/SignalServiceKit/Network/API/Giphy/GiphyAsset.swift +++ b/SignalServiceKit/Network/API/Giphy/GiphyAsset.swift @@ -4,7 +4,7 @@ // import Foundation -import CoreServices +import UniformTypeIdentifiers public class GiphyAsset: ProxiedContentAssetDescription { let rendition: Rendition @@ -114,9 +114,9 @@ extension GiphyAsset { public var utiType: String { switch self { - case .jpg: return kUTTypeJPEG as String - case .gif: return kUTTypeGIF as String - case .mp4: return kUTTypeMPEG4 as String + case .jpg: return UTType.jpeg.identifier + case .gif: return UTType.gif.identifier + case .mp4: return UTType.mpeg4Movie.identifier } } } diff --git a/SignalServiceKit/Profiles/BadgeAssets.swift b/SignalServiceKit/Profiles/BadgeAssets.swift index 014a94ddad..ee9240d9ab 100644 --- a/SignalServiceKit/Profiles/BadgeAssets.swift +++ b/SignalServiceKit/Profiles/BadgeAssets.swift @@ -6,6 +6,7 @@ import CoreServices import Foundation import ImageIO +import UniformTypeIdentifiers @objc public class BadgeAssets: NSObject { @@ -148,7 +149,7 @@ public class BadgeAssets: NSObject { guard !OWSFileSystem.fileOrFolderExists(url: destinationUrl) else { return } guard let spriteImage = spriteParser.copySprite(variant: variant), - let imageDestination = CGImageDestinationCreateWithURL(destinationUrl as CFURL, kUTTypePNG, 1, nil) else { + let imageDestination = CGImageDestinationCreateWithURL(destinationUrl as CFURL, UTType.png.identifier as CFString, 1, nil) else { throw OWSAssertionError("Couldn't load image") } CGImageDestinationAddImage(imageDestination, spriteImage, nil) diff --git a/SignalServiceKit/Util/MimeTypeUtil.swift b/SignalServiceKit/Util/MimeTypeUtil.swift index 5ae8b95633..3998a30ec6 100644 --- a/SignalServiceKit/Util/MimeTypeUtil.swift +++ b/SignalServiceKit/Util/MimeTypeUtil.swift @@ -5,6 +5,7 @@ import CoreServices import Foundation +import UniformTypeIdentifiers public enum MimeType: String { case applicationJson = "application/json" @@ -164,12 +165,14 @@ public class MimeTypeUtil: NSObject { // MARK: - Conversion Functions for UTI Type / MIME Type / File Extension public static func utiTypeForMimeType(_ mimeType: String) -> String? { - UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeRetainedValue() as String? + UTType(mimeType: mimeType)?.identifier } + public static func utiTypeForFileExtension(_ fileExtension: String) -> String? { owsAssertDebug(!fileExtension.isEmpty) - return UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil)?.takeRetainedValue() as String? + return UTType(filenameExtension: fileExtension)?.identifier } + @objc public static func mimeTypeForFileExtension(_ fileExtension: String) -> String? { owsAssertDebug(!fileExtension.isEmpty) @@ -183,7 +186,7 @@ public class MimeTypeUtil: NSObject { if utiType == "public.aac-audio" { return "m4a" } else { - return UTTypeCopyPreferredTagWithClass(utiType as CFString, kUTTagClassFilenameExtension)?.takeRetainedValue() as String? + return UTType(utiType)?.preferredFilenameExtension } } @objc diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index c7dd621da9..f5c6511d8f 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -8,6 +8,7 @@ import Intents import PureLayout import SignalServiceKit import SignalUI +import UniformTypeIdentifiers public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailedViewDelegate { @@ -574,8 +575,8 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed // Handle safari sharing images and PDFs as two separate items one with the object to share and the other as the URL of the data. for extensionItem in extensionItems { for attachment in extensionItem.attachments ?? [] { - if attachment.hasItemConformingToTypeIdentifier(kUTTypeData as String) - || attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) + if attachment.hasItemConformingToTypeIdentifier(UTType.data.identifier) + || attachment.hasItemConformingToTypeIdentifier(UTType.fileURL.identifier) || attachment.hasItemConformingToTypeIdentifier("com.apple.pkpass") { return extensionItem } @@ -599,23 +600,23 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed var typeIdentifier: String { switch self { case .movie: - return kUTTypeMovie as String + return UTType.movie.identifier case .image: - return kUTTypeImage as String + return UTType.image.identifier case .webUrl: - return kUTTypeURL as String + return UTType.url.identifier case .fileUrl: - return kUTTypeFileURL as String + return UTType.fileURL.identifier case .contact: - return kUTTypeVCard as String + return UTType.vCard.identifier case .text: - return kUTTypeText as String + return UTType.text.identifier case .pdf: - return kUTTypePDF as String + return UTType.pdf.identifier case .pkPass: return "com.apple.pkpass" case .data: - return kUTTypeData as String + return UTType.data.identifier } } } @@ -710,9 +711,9 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed let url: NSURL = try await Self.loadObjectWithKeyedUnarchiverFallback(fromItemProvider: itemProvider, forTypeIdentifier: typedItemProvider.itemType.typeIdentifier, cannotLoadError: .cannotLoadURLObject, failedLoadError: .loadURLObjectFailed) return try Self.createAttachment(withText: (url as URL).absoluteString) case .contact: - let contactData = try await Self.loadDataRepresentation(fromItemProvider: itemProvider, forTypeIdentifier: kUTTypeContact as String) - let dataSource = DataSourceValue.dataSource(with: contactData, utiType: kUTTypeContact as String) - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeContact as String) + let contactData = try await Self.loadDataRepresentation(fromItemProvider: itemProvider, forTypeIdentifier: UTType.contact.identifier) + let dataSource = DataSourceValue.dataSource(with: contactData, utiType: UTType.contact.identifier) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: UTType.contact.identifier) attachment.isConvertibleToContactShare = true if let attachmentError = attachment.error { throw attachmentError @@ -733,7 +734,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed } } - nonisolated private static func copyAttachment(fromUrl url: URL, defaultTypeIdentifier: String = kUTTypeData as String) throws -> SignalAttachment { + nonisolated private static func copyAttachment(fromUrl url: URL, defaultTypeIdentifier: String = UTType.data.identifier) throws -> SignalAttachment { guard let dataSource = try? DataSourcePath.dataSource(with: url, shouldDeleteOnDeallocation: false) else { throw ShareViewControllerError.nonFileUrl } @@ -843,7 +844,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed nonisolated private static func createAttachment(withText text: String) throws -> SignalAttachment { let dataSource = DataSourceValue.dataSource(withOversizeText: text) - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeText as String) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: UTType.text.identifier) if let attachmentError = attachment.error { throw attachmentError } @@ -855,9 +856,9 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed guard let imagePng = image.pngData() else { throw ShareViewControllerError.uiImageMissingOrCorruptImageData } - let typeIdentifier = kUTTypePNG as String - let dataSource = DataSourceValue.dataSource(with: imagePng, utiType: typeIdentifier) - let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: typeIdentifier) + let type = UTType.png + let dataSource = DataSourceValue.dataSource(with: imagePng, utiType: type.identifier) + let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: type.identifier) if let attachmentError = attachment.error { throw attachmentError } diff --git a/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift b/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift index f8d990c043..6c0fa0dc37 100644 --- a/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift +++ b/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift @@ -684,21 +684,21 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC } return dstImage }.map(on: DispatchQueue.global()) { (dstImage: UIImage) -> SignalAttachment in - var dataUTI = kUTTypeImage as String + var dataType = UTType.image guard let dstData: Data = { let isLossy: Bool = attachmentApprovalItem.attachment.mimeType.caseInsensitiveCompare(MimeType.imageJpeg.rawValue) == .orderedSame if isLossy { - dataUTI = kUTTypeJPEG as String + dataType = .jpeg return dstImage.jpegData(compressionQuality: 0.9) } else { - dataUTI = kUTTypePNG as String + dataType = .png return dstImage.pngData() } }() else { owsFailDebug("Could not export for output.") return attachmentApprovalItem.attachment } - guard let dataSource = DataSourceValue.dataSource(with: dstData, utiType: dataUTI) else { + guard let dataSource = DataSourceValue.dataSource(with: dstData, utiType: dataType.identifier) else { owsFailDebug("Could not prepare data source for output.") return attachmentApprovalItem.attachment } @@ -706,13 +706,13 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC // Rewrite the filename's extension to reflect the output file format. var filename: String? = attachmentApprovalItem.attachment.sourceFilename if let sourceFilename = attachmentApprovalItem.attachment.sourceFilename { - if let fileExtension: String = MimeTypeUtil.fileExtensionForUtiType(dataUTI) { + if let fileExtension: String = MimeTypeUtil.fileExtensionForUtiType(dataType.identifier) { filename = (sourceFilename as NSString).deletingPathExtension.appendingFileExtension(fileExtension) } } dataSource.sourceFilename = filename - let dstAttachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: dataUTI) + let dstAttachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: dataType.identifier) if let attachmentError = dstAttachment.error { owsFailDebug("Could not prepare attachment for output: \(attachmentError).") return attachmentApprovalItem.attachment