Only use the appGroupTemporaryDirectory to access a file from the share extension. (#4002)

… and switch back to the plain `URL.temporaryDirectory` for everything else.

* Fix documentation

Co-authored-by: Stefan Ceriu <stefan.ceriu@gmail.com>
This commit is contained in:
Doug
2025-04-10 09:56:25 +01:00
committed by GitHub
parent 58a0709a1b
commit 9ba4048462
6 changed files with 99 additions and 27 deletions
@@ -37,8 +37,9 @@ extension FileManager {
}
@discardableResult
func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL {
let newURL = URL.appGroupTemporaryDirectory.appendingPathComponent(fileName)
func writeDataToTemporaryDirectory(data: Data, fileName: String, withinAppGroupContainer: Bool = false) throws -> URL {
let baseURL: URL = withinAppGroupContainer ? .appGroupTemporaryDirectory : .temporaryDirectory
let newURL = baseURL.appendingPathComponent(fileName)
try data.write(to: newURL)
@@ -28,20 +28,23 @@ extension NSItemProvider {
try? await loadItem(forTypeIdentifier: UTType.text.identifier) as? String
}
func storeData() async -> URL? {
/// Stores the item's data from the provider within the temporary directory, returning the URL on success.
/// - Parameter withinAppGroupContainer: Whether the data needs to be shared between bundles.
/// If passing `true` you will need to manually clean up the file once you have the data in the receiving bundle.
func storeData(withinAppGroupContainer: Bool = false) async -> URL? {
guard let contentType = preferredContentType else {
MXLog.error("Invalid NSItemProvider: \(self)")
return nil
}
if contentType.type.identifier == UTType.image.identifier {
return await generateURLForUIImage(contentType)
return await generateURLForUIImage(contentType, withinAppGroupContainer: withinAppGroupContainer)
} else {
return await generateURLForGenericData(contentType)
return await generateURLForGenericData(contentType, withinAppGroupContainer: withinAppGroupContainer)
}
}
private func generateURLForUIImage(_ contentType: PreferredContentType) async -> URL? {
private func generateURLForUIImage(_ contentType: PreferredContentType, withinAppGroupContainer: Bool) async -> URL? {
guard let uiImage = try? await loadItem(forTypeIdentifier: contentType.type.identifier) as? UIImage else {
MXLog.error("Failed casting UIImage, invalid NSItemProvider: \(self)")
return nil
@@ -52,22 +55,25 @@ extension NSItemProvider {
return nil
}
let filename = if let suggestedName = suggestedName as NSString?,
// Suggestions are nice but their extension is `jpeg`
let filename = (suggestedName.deletingPathExtension as NSString).appendingPathExtension(contentType.fileExtension) {
filename
} else {
"\(UUID().uuidString).\(contentType.fileExtension)"
}
do {
if let suggestedName = suggestedName as? NSString,
// Suggestions are nice but their extension is `jpeg`
let filename = (suggestedName.deletingPathExtension as NSString).appendingPathExtension(contentType.fileExtension) {
return try FileManager.default.writeDataToTemporaryDirectory(data: pngData, fileName: filename)
} else {
let filename = "\(UUID().uuidString).\(contentType.fileExtension)"
return try FileManager.default.writeDataToTemporaryDirectory(data: pngData, fileName: filename)
}
return try FileManager.default.writeDataToTemporaryDirectory(data: pngData,
fileName: filename,
withinAppGroupContainer: withinAppGroupContainer)
} catch {
MXLog.error("Failed storing NSItemProvider data \(self) with error: \(error)")
return nil
}
}
private func generateURLForGenericData(_ contentType: PreferredContentType) async -> URL? {
private func generateURLForGenericData(_ contentType: PreferredContentType, withinAppGroupContainer: Bool) async -> URL? {
let providerDescription = description
let shareData: Data? = await withCheckedContinuation { continuation in
_ = loadDataRepresentation(for: contentType.type) { data, error in
@@ -92,15 +98,19 @@ extension NSItemProvider {
return nil
}
let filename = if let suggestedName = suggestedName as NSString?,
suggestedName.hasPathExtension {
suggestedName as String
} else if let suggestedName {
"\(suggestedName).\(contentType.fileExtension)"
} else {
"\(UUID().uuidString).\(contentType.fileExtension)"
}
do {
if let filename = suggestedName {
let hasExtension = !(filename as NSString).pathExtension.isEmpty
let filename = hasExtension ? filename : "\(filename).\(contentType.fileExtension)"
return try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
} else {
let filename = "\(UUID().uuidString).\(contentType.fileExtension)"
return try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
}
return try FileManager.default.writeDataToTemporaryDirectory(data: shareData,
fileName: filename,
withinAppGroupContainer: withinAppGroupContainer)
} catch {
MXLog.error("Failed storing NSItemProvider data \(self) with error: \(error)")
return nil
@@ -164,3 +174,7 @@ extension NSItemProvider {
return mimeType.hasPrefix("image/") || mimeType.hasPrefix("video/") || mimeType.hasPrefix("application/")
}
}
private extension NSString {
var hasPathExtension: Bool { !pathExtension.isEmpty }
}
+4 -1
View File
@@ -75,7 +75,10 @@ extension URL: @retroactive ExpressibleByStringLiteral {
return url
}
/// The app group temporary directory
/// The app group temporary directory (useful for transferring files between different bundles).
///
/// **Note:** This `tmp` directory doesn't appear to behave as expected as it isn't being tidied up by the system.
/// Make sure to manually tidy up any files you place in here once you've transferred them from one bundle to another.
static var appGroupTemporaryDirectory: URL {
let url = appGroupContainerDirectory
.appendingPathComponent("tmp", isDirectory: true)