在 Mac Catalyst 应用程序中使用 UIDocumentPickerViewController 读取文件需要 read/write 授权?
Reading files with UIDocumentPickerViewController in a Mac Catalyst app requires read/write entitlement?
我在 UIViewController
中有此代码,确认 UIDocumentPickerDelegate
:
- (void)openTextFilePicker {
NSArray *UTIs = [NSArray arrayWithObjects:@"public.text", nil];
[self openFilePicker:UTIs];
}
- (void)openFilePicker:(NSArray *)UTIs {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:UTIs inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.popoverPresentationController.barButtonItem = self.importButton;
[self presentViewController:documentPicker animated:TRUE completion:nil];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURLs:(NSArray<NSURL *> *)urls {
[self documentPicker:controller didPickDocumentAtURL:[urls firstObject]];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
NSLog(@"picked document %@", url);
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
NSLog(@"cancelled");
}
这允许用户 select 系统上任何位置的文件并将其内容导入应用程序。如果我将 App Sandbox > User Selected File 功能设置为 Read/Write,它就会工作。但我只是从文件中读取数据,不需要更新文件或在沙箱外保存任何内容。所以我将功能更改为只读,但随后我在控制台中收到此错误,并且未调用 didPickDocumentsAtURLs
:
Failed to create an FPSandboxingURLWrapper for [path]. Error: Error Domain=NSPOSIXErrorDomain Code=1 "couldn't issue sandbox extension com.apple.app-sandbox.read-write for '[path]': Operation not permitted"
这看起来是否正确,或者我是否应该能够仅使用只读权限读取文件?如果需要,将其切换为 Read/Write 很容易,但我的第一次提交因使用了不必要的权利而被拒绝,如果确实需要,我想准备向审核团队证明这一点。
我继续将权利更改为 Read/Write,并在 App Sandbox Information 字段中附上一条注释来提交它,解释如何使用文件访问权限。该应用已获批准。
对于搜索此内容的任何人,我没有通过陈述我的案例获得应用批准 - 相反,我被迫找到以下解决方案 - 它使用 UIDocumentBrowserViewController(只读!)与 UIDocumentPickerViewController(需要阅读 -写)
import Foundation
import UIKit
extension ViewController: UIDocumentBrowserViewControllerDelegate, UIDocumentPickerDelegate {
@objc func presentDocumentPicker() {
if operatingSystem == .macintosh {
let documentPicker = UIDocumentBrowserViewController(forOpening: [.pdf])
documentPicker.delegate = self
documentPicker.allowsDocumentCreation = false
documentPicker.allowsPickingMultipleItems = false
// Present the document picker.
present(documentPicker, animated: true, completion: nil)
} else {
let documentsPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.pdf])
documentsPicker.delegate = self
documentsPicker.allowsMultipleSelection = false
documentsPicker.modalPresentationStyle = .fullScreen
self.present(documentsPicker, animated: true, completion: nil)
}
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
guard let url = documentURLs.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true)
}
}
我在 UIViewController
中有此代码,确认 UIDocumentPickerDelegate
:
- (void)openTextFilePicker {
NSArray *UTIs = [NSArray arrayWithObjects:@"public.text", nil];
[self openFilePicker:UTIs];
}
- (void)openFilePicker:(NSArray *)UTIs {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:UTIs inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.popoverPresentationController.barButtonItem = self.importButton;
[self presentViewController:documentPicker animated:TRUE completion:nil];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURLs:(NSArray<NSURL *> *)urls {
[self documentPicker:controller didPickDocumentAtURL:[urls firstObject]];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
NSLog(@"picked document %@", url);
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
NSLog(@"cancelled");
}
这允许用户 select 系统上任何位置的文件并将其内容导入应用程序。如果我将 App Sandbox > User Selected File 功能设置为 Read/Write,它就会工作。但我只是从文件中读取数据,不需要更新文件或在沙箱外保存任何内容。所以我将功能更改为只读,但随后我在控制台中收到此错误,并且未调用 didPickDocumentsAtURLs
:
Failed to create an FPSandboxingURLWrapper for [path]. Error: Error Domain=NSPOSIXErrorDomain Code=1 "couldn't issue sandbox extension com.apple.app-sandbox.read-write for '[path]': Operation not permitted"
这看起来是否正确,或者我是否应该能够仅使用只读权限读取文件?如果需要,将其切换为 Read/Write 很容易,但我的第一次提交因使用了不必要的权利而被拒绝,如果确实需要,我想准备向审核团队证明这一点。
我继续将权利更改为 Read/Write,并在 App Sandbox Information 字段中附上一条注释来提交它,解释如何使用文件访问权限。该应用已获批准。
对于搜索此内容的任何人,我没有通过陈述我的案例获得应用批准 - 相反,我被迫找到以下解决方案 - 它使用 UIDocumentBrowserViewController(只读!)与 UIDocumentPickerViewController(需要阅读 -写)
import Foundation
import UIKit
extension ViewController: UIDocumentBrowserViewControllerDelegate, UIDocumentPickerDelegate {
@objc func presentDocumentPicker() {
if operatingSystem == .macintosh {
let documentPicker = UIDocumentBrowserViewController(forOpening: [.pdf])
documentPicker.delegate = self
documentPicker.allowsDocumentCreation = false
documentPicker.allowsPickingMultipleItems = false
// Present the document picker.
present(documentPicker, animated: true, completion: nil)
} else {
let documentsPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.pdf])
documentsPicker.delegate = self
documentsPicker.allowsMultipleSelection = false
documentsPicker.modalPresentationStyle = .fullScreen
self.present(documentsPicker, animated: true, completion: nil)
}
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
guard let url = documentURLs.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true)
}
}