Skip to content

Commit 67bb7f4

Browse files
lucasfernognashaofu
authored andcommitted
fix(dialog): do not create file copy for save file picker on iOS
On iOS the file picker returns a security scoped resource file path on the save() file picker: https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller?language=objc#Work-with-external-documents this means we can't directly access it without calling [startAccessingSecurityScopedResource](https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc) this PR changes the plugin to not access the save file early, leaving that to the user and returning its actual path # Conflicts: # examples/api/src-tauri/gen/apple/api.xcodeproj/project.pbxproj # plugins/dialog/ios/Sources/DialogPlugin.swift
1 parent 30dd109 commit 67bb7f4

File tree

3 files changed

+39
-46
lines changed

3 files changed

+39
-46
lines changed

plugins/dialog/guest-js/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ type OpenDialogReturn<T extends OpenDialogOptions> = T['directory'] extends true
280280
* }
281281
* ```
282282
*
283+
* ## Platform-specific
284+
*
285+
* - **iOS**: Returns a copy of the file to bypass [security scoped resource](https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc).
286+
*
283287
* @returns A promise resolving to the selected path(s)
284288
*
285289
* @since 2.0.0
@@ -314,6 +318,10 @@ async function open<T extends OpenDialogOptions>(
314318
* });
315319
* ```
316320
*
321+
* #### Platform-specific
322+
*
323+
* - **iOS**: Returns a copy of the file to bypass [security scoped resource](https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc).
324+
*
317325
* @returns A promise resolving to the selected path.
318326
*
319327
* @since 2.0.0

plugins/dialog/ios/Sources/FilePickerController.swift

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -95,35 +95,11 @@ public class FilePickerController: NSObject {
9595
return nil
9696
}
9797
}
98-
99-
private func saveTemporaryFile(_ sourceUrl: URL) throws -> URL {
100-
var directory = URL(fileURLWithPath: NSTemporaryDirectory())
101-
if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
102-
directory = cachesDirectory
103-
}
104-
let targetUrl = directory.appendingPathComponent(sourceUrl.lastPathComponent)
105-
do {
106-
try deleteFile(targetUrl)
107-
}
108-
try FileManager.default.copyItem(at: sourceUrl, to: targetUrl)
109-
return targetUrl
110-
}
111-
112-
private func deleteFile(_ url: URL) throws {
113-
if FileManager.default.fileExists(atPath: url.path) {
114-
try FileManager.default.removeItem(atPath: url.path)
115-
}
116-
}
11798
}
11899

119100
extension FilePickerController: UIDocumentPickerDelegate {
120101
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
121-
do {
122-
let temporaryUrls = try urls.map { try saveTemporaryFile($0) }
123-
self.plugin.onFilePickerEvent(.selected(temporaryUrls))
124-
} catch {
125-
self.plugin.onFilePickerEvent(.error("Failed to create a temporary copy of the file"))
126-
}
102+
self.plugin.onFilePickerEvent(.selected(urls))
127103
}
128104

129105
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
@@ -148,12 +124,7 @@ extension FilePickerController: UIImagePickerControllerDelegate, UINavigationCon
148124
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
149125
dismissViewController(picker) {
150126
if let url = info[.mediaURL] as? URL {
151-
do {
152-
let temporaryUrl = try self.saveTemporaryFile(url)
153-
self.plugin.onFilePickerEvent(.selected([temporaryUrl]))
154-
} catch {
155-
self.plugin.onFilePickerEvent(.error("Failed to create a temporary copy of the file"))
156-
}
127+
self.plugin.onFilePickerEvent(.selected([url]))
157128
} else {
158129
self.plugin.onFilePickerEvent(.cancelled)
159130
}
@@ -169,7 +140,7 @@ extension FilePickerController: PHPickerViewControllerDelegate {
169140
self.plugin.onFilePickerEvent(.cancelled)
170141
return
171142
}
172-
var temporaryUrls: [URL] = []
143+
var urls: [URL] = []
173144
var errorMessage: String?
174145
let dispatchGroup = DispatchGroup()
175146
for result in results {
@@ -190,12 +161,7 @@ extension FilePickerController: PHPickerViewControllerDelegate {
190161
errorMessage = "Unknown error"
191162
return
192163
}
193-
do {
194-
let temporaryUrl = try self.saveTemporaryFile(url)
195-
temporaryUrls.append(temporaryUrl)
196-
} catch {
197-
errorMessage = "Failed to create a temporary copy of the file"
198-
}
164+
urls.append(url)
199165
})
200166
} else if result.itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
201167
dispatchGroup.enter()
@@ -211,12 +177,7 @@ extension FilePickerController: PHPickerViewControllerDelegate {
211177
errorMessage = "Unknown error"
212178
return
213179
}
214-
do {
215-
let temporaryUrl = try self.saveTemporaryFile(url)
216-
temporaryUrls.append(temporaryUrl)
217-
} catch {
218-
errorMessage = "Failed to create a temporary copy of the file"
219-
}
180+
urls.append(url)
220181
})
221182
} else {
222183
errorMessage = "Unsupported file type identifier"
@@ -227,7 +188,7 @@ extension FilePickerController: PHPickerViewControllerDelegate {
227188
self.plugin.onFilePickerEvent(.error(errorMessage))
228189
return
229190
}
230-
self.plugin.onFilePickerEvent(.selected(temporaryUrls))
191+
self.plugin.onFilePickerEvent(.selected(urls))
231192
}
232193
}
233-
}
194+
}

plugins/dialog/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
485485
/// Ok(())
486486
/// });
487487
/// ```
488+
///
489+
/// ## Platform-specific
490+
///
491+
/// - **iOS**: Returns a copy of the file to bypass [security scoped resource].
492+
///
493+
/// [security scoped resource]: https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc
488494
pub fn pick_file<F: FnOnce(Option<FilePath>) + Send + 'static>(self, f: F) {
489495
pick_file(self, f)
490496
}
@@ -596,6 +602,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
596602
/// Ok(())
597603
/// });
598604
/// ```
605+
///
606+
/// ## Platform-specific
607+
///
608+
/// - **iOS**: Returns a [security scoped resource] so you must request access before reading or writing to the file.
609+
///
610+
/// [security scoped resource]: https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc
599611
pub fn save_file<F: FnOnce(Option<FilePath>) + Send + 'static>(self, f: F) {
600612
save_file(self, f)
601613
}
@@ -618,6 +630,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
618630
/// // the file path is `None` if the user closed the dialog
619631
/// }
620632
/// ```
633+
///
634+
/// ## Platform-specific
635+
///
636+
/// - **iOS**: Returns a copy of the file to bypass [security scoped resource].
637+
///
638+
/// [security scoped resource]: https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc
621639
pub fn blocking_pick_file(self) -> Option<FilePath> {
622640
blocking_fn!(self, pick_file)
623641
}
@@ -696,6 +714,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
696714
/// // the file path is `None` if the user closed the dialog
697715
/// }
698716
/// ```
717+
///
718+
/// ## Platform-specific
719+
///
720+
/// - **iOS**: Returns a [security scoped resource] so you must request access before reading or writing to the file.
721+
///
722+
/// [security scoped resource]: https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc
699723
pub fn blocking_save_file(self) -> Option<FilePath> {
700724
blocking_fn!(self, save_file)
701725
}

0 commit comments

Comments
 (0)