https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
"Use a document picker to access the content of a directory outside your app’s container."
앱 컨테이너 외부에 있는 디렉토리의 컨텐트에 접근하기 위해 문서 picker를 사용합니다.
iOS 12 및 이전 버전에서 사용자는 앱 컨테이너 외부에서 파일을 열 수 있고 상호작용할 수 있습니다. UIDocumentBrowserViewController
와 UIDocumentPickerViewController
는 시스템의 로컬 파일 제공자, 아이클라우드, 혹은 파일 제공자 확장을 사용하는 써드파티 서비스에서 파일에 접근권한을 제공합니다. 사용자는 동시에 여러 파일을 선택할 수 있습니다. 하지만 각 파일을 하나씩 선택해야 합니다.
iOS 13에서 사용자는 UIDocumentPickerViewController
를 사용하는 모든 사용 가능한 파일 제공자로부터 디렉토리를 선택할 수 있습니다. 문서 picker는 앱이 컨테이너 외부에서 컨텐트에 접근하는 것을 허용해주는 디렉토리에 대한 security-scoped URL을 반환합니다. 이 경우 URL은 앱이 재귀적으로 디렉토리 및 모든 컨텐츠에 접근할 수 있도록 해주고, 나중에 디렉토리에 추가되는 모든 새 아이템 접근 역시 가능하게 해줍니다. 이 URL에 대해 다음 launch에서 디렉토리에 접근할 수 있도록 해주는 북마크를 저장할 수 있습니다.
사용자에게 디렉토리 선택을 프롬프트하려면, 문서 picker를 생성하고 타입 폴더로 열기 위한 컨텐트 타입을 설정해야 합니다. 이후 문서 picker의 딜리게이트를 설정하고 제시해야 합니다.
// Create a document picker for directories.
let documentPicker =
UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
documentPicker.delegate = self
// Set the initial directory.
documentPicker.directoryURL = startingDirectory
// Present the document picker.
present(documentPicker, animated: true, completion: nil)
present(_:animated:completion:)
메소드를 호출하는 즉시 시스템은 사용자에게 문서 picker를 보여줍니다. directoryURL
속성을 구체화하는 경우 문서 picker는 선택된 디렉토리로 시작합니다. 반대의 경우 사용자가 선택했던 가장 최근의 디렉토리로 시작합니다.
사용자가 완료를 탭하면 시스템은 사용자가 선택한 디렉토리에 대한 security-scoped URL의 배열을 전달하면서, 딜리게이트의 documentPicker(_:didPickDocumentsAt:)
메소드를 호출합니다. 디렉토리 및 하위 디렉토리의 컨텐트를 열거하기 위해 security-scoped URL을 사용할 수 있으며, 모든 파일에 대한 추가, 제거, 수정을 위해서도 security-scoped URL을 사용할 수 있습니다.
만약 사용자가 취소를 탭하면 시스템은 대신 documentPickerWasCancelled(_:)
를 호출합니다.
Note
UIDocumentBrowserViewController
는 폴더 문서 타입을 지원하지 않습니다. 디렉토리에 접근권한을 제공하려면UIDocumentPickerViewController
를 대신 사용하시기 바랍니다.
문서 picker에서 사용자가 디렉토리를 선택하면, 시스템은 앱에게 해당 디렉토리 및 이 디렉토리의 모든 컨텐츠에 대한 접근권한 허용을 줍니다. 문서 picker는 디렉토리에 대한 security-scoped URL을 반환합니다. 디렉토리의 컨텐트를 열거하기 위해 이와 같은 URL 중 한 가지를 사용하는 경우 결과 URL 역시 security-scoped 입니다. security-scoped URL을 북마크로 저장할 수 있고, 이후 security-scoped로 다시 확인할 수 있습니다.
security-scoped URL의 컨텐트에 접근하려면 아래 내용을 수행해야 합니다.
startAccessingSecurityScopedResource()
를 호출합니다.stopAccessingSecurityScopedResource()
를 호출합니다.func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
// Start accessing a security-scoped resource.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
// Make sure you release the security-scoped resource when you finish.
defer { url.stopAccessingSecurityScopedResource() }
// Use file coordination for reading and writing any of the URL’s content.
var error: NSError? = nil
NSFileCoordinator().coordinate(readingItemAt: url, error: &error) { (url) in
let keys : [URLResourceKey] = [.nameKey, .isDirectoryKey]
// Get an enumerator for the directory's content.
guard let fileList =
FileManager.default.enumerator(at: url, includingPropertiesForKeys: keys) else {
Swift.debugPrint("*** Unable to access the contents of \(url.path) ***\n")
return
}
for case let file as URL in fileList {
// Start accessing the content's security-scoped URL.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
continue
}
// Do something with the file here.
Swift.debugPrint("chosen file: \(file.lastPathComponent)")
// Make sure you release the security-scoped resource when you finish.
url.stopAccessingSecurityScopedResource()
}
}
}
나중에 URL에 접근하려면 bookmarkData(options:includingResourceValuesForKeys:relativeTo:)
메소드를 사용해서 URL을 minimalBookmark
로 저장해야 합니다.
do {
// Start accessing a security-scoped resource.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
// Make sure you release the security-scoped resource when you finish.
defer { url.stopAccessingSecurityScopedResource() }
let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
try bookmarkData.write(to: getMyURLForBookmark())
}
catch let error {
// Handle the error here.
}
이후 북마크를 읽을 수 있고, 다시 security-scoped URL로 확인할 수 있습니다.
do {
let bookmarkData = try Data(contentsOf: getMyURLForBookmark())
var isStale = false
let url = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
guard !isStale else {
// Handle stale data here.
return
}
// Use the URL here.
}
catch let error {
// Handle the error here.
}
사용자는 항상 디렉토리에 접근할 수 있는 앱에 대해 완료된 컨트롤을 갖습니다. 문서 picker로부터 디렉토리 선택 후 앱은 설정 > 보안 > 파일 및 폴더에서 나타납니다. 이 페이지는 공유된 디렉토리에 접근 권한을 승인한 모든 앱의 리스트를 갖고 있고, 사용자는 언제든지 각 앱에 대한 접근권한 설정을 변경시킬 수 있습니다.
이는 디렉토리의 컨텐트에 접근할 때, 앱이 이미 실패를 처리할 준비가 되어 있음을 의미합니다. startAccessingSecurityScopedResource()
메소드 호출과 URL에 대한 읽기 및 쓰기 시도 역시 실패할 수 있습니다. security-scoped URL에 북마크로 저장하거나 확인할 때 특히 그렇습니다. 왜냐하면 북마크를 사용하는 경우 사용자가 앱 권한을 변경시키는 데 많은 시간이 걸릴 수 있기 때문입니다.
사용자에게 앱에서 로컬 혹은 원격 문서에 대한 접근 권한을 제공합니다.
https://developer.apple.com/documentation/uikit/view_controllers/adding_a_document_browser_to_your_app
https://velog.io/@panther222128/Adding-a-Document-Browser-to-Your-App
로컬 및 클라우드에 저장한 문서에 브라우징하거나 액션을 수행하기 위한 뷰 컨트롤러입니다.
https://developer.apple.com/documentation/uikit/uidocumentbrowserviewcontroller
https://velog.io/@panther222128/UIDocumentBrowserViewController
앱 샌드박스의 외부에 있는 문서 혹은 목적지에 접근권한을 제공하는 뷰 컨트롤러입니다.
https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller
https://velog.io/@panther222128/UIDocumentPickerViewController
앱이 직접 처리할 수 없는 파일 포맷으로 프리뷰, 열기, 프린트 파일을 하는 뷰 컨트롤러입니다.
https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller
https://velog.io/@panther222128/UIDocumentInteractionController