SwiftUI から UIDocumentPickerViewController を呼び出す
概要
SwiftUI から UIDocumentPickerViewController (opens in a new tab) を呼び出す方法を説明します。 具体的には以下を実現します。
- ユーザにフォルダーを選択させ、そこにファイルを作成する。
- ユーザにファイルを選択させ、その内容を読み取る。
UIDocumentPickerViewController を UIViewControllerRepresentable でラップする
UIDocumentPickerViewController を UIViewControllerRepresentable でラップします。
初期化の引数として、UIDocumentPickerViewController の引数である、openingContentTypes
とasCopy
を受け取ります。
DocumentPickerView.swift
import SwiftUI
import UniformTypeIdentifiers
struct DocumentPickerView : UIViewControllerRepresentable {
let openingContentTypes: [UTType]
let asCopy: Bool
private var didPickDocumentCallback: ((URL) -> Void)?
init(openingContentTypes: [UTType], asCopy: Bool = false) {
self.openingContentTypes = openingContentTypes
self.asCopy = asCopy
}
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
let documentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: openingContentTypes, asCopy: asCopy)
documentPickerViewController.delegate = context.coordinator
return documentPickerViewController
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
/// ドキュメントが選択された際のコールバックを指定します
func didPickDocument(callback: @escaping (URL) -> Void) -> Self {
var view = self
view.didPickDocumentCallback = callback
return view
}
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent: DocumentPickerView
init(_ parent: DocumentPickerView) {
self.parent = parent
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
self.parent.didPickDocumentCallback?(url)
}
}
}
SwiftUI からの呼び出し方
UserDictionaryView.swift
import SwiftUI
import UniformTypeIdentifiers
struct UserDictionaryView: View {
@State private var showsImportDocumentPicker = false
@State private var showsExportDocumentPicker = false
var body: some View {
VStack(spacing: 0) {
Button("exportToFile", action: {
showsExportDocumentPicker = true
})
Button("importFromFile", action: {
showsImportDocumentPicker = true
})
}
.sheet(isPresented: $showsExportDocumentPicker) {
// 1. ユーザにフォルダーを選択させ、そこにファイルを作成する。
DocumentPickerView(openingContentTypes: [UTType.folder])
.didPickDocument { directoryURL in
guard directoryURL.startAccessingSecurityScopedResource() else { return }
defer { directoryURL.stopAccessingSecurityScopedResource() }
let newFileURL = directoryURL.appendingPathComponent("fileName.tsv")
do {
try "a,b,c".write(to: newFileURL, atomically: true, encoding: .utf8)
} catch {
print("Failed to export")
}
}
}
.sheet(isPresented: $showsImportDocumentPicker) {
// 2. ユーザにファイルを選択させ、その内容を読み取る。
DocumentPickerView(openingContentTypes: [UTType.text])
.didPickDocument { fileURL in
do {
guard fileURL.startAccessingSecurityScopedResource() else { return }
defer { fileURL.stopAccessingSecurityScopedResource() }
let fileContent = try String(contentsOf: fileURL, encoding: .utf8)
print(fileContent)
} catch {
print("Failed to import")
}
}
}
}
}
参考
- Android 版の実装はこちら