diff --git a/Modules/Sources/Browse/UI/Components/SaveDocumentPicker.swift b/Modules/Sources/Browse/UI/Components/SaveDocumentPicker.swift new file mode 100644 index 0000000..3e1bec5 --- /dev/null +++ b/Modules/Sources/Browse/UI/Components/SaveDocumentPicker.swift @@ -0,0 +1,120 @@ +// +// SaveDocumentPicker.swift +// Browse +// +// Created by Javier Cicchelli on 17/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +import Foundation +import SwiftUI +import UIKit + +struct SaveDocumentPicker: UIViewControllerRepresentable { + + // MARK: Type aliases + + typealias DownloadedClosure = (NSError?) -> Void + typealias CancelledClosure = () -> Void + + // MARK: Properties + + private let fileName: String + private let fileData: Data + private let downloaded: DownloadedClosure + private let cancelled: CancelledClosure? + + // MARK: Initialisers + + init( + fileName: String, + fileData: Data, + downloaded: @escaping DownloadedClosure, + cancelled: CancelledClosure? = nil + ) { + self.fileName = fileName + self.fileData = fileData + self.downloaded = downloaded + self.cancelled = cancelled + } + + // MARK: Functions + + func makeUIViewController(context: Context) -> UIDocumentPickerViewController { + let controller = UIDocumentPickerViewController( + forOpeningContentTypes: [.folder] + ) + + controller.allowsMultipleSelection = false + controller.shouldShowFileExtensions = false + controller.delegate = context.coordinator + + return controller + } + + func updateUIViewController( + _ uiViewController: UIDocumentPickerViewController, + context: Context + ) { } + + func makeCoordinator() -> Coordinator { + .init(self) + } +} + +// MARK: - Coordinators + +extension SaveDocumentPicker { + class Coordinator: NSObject, UIDocumentPickerDelegate { + + // MARK: Properties + + private let parent: SaveDocumentPicker + private let fileCoordinator: NSFileCoordinator = .init() + private let fileManager: FileManager = .default + + // MARK: Initialisers + + init(_ parent: SaveDocumentPicker) { + self.parent = parent + } + + // MARK: UIDocumentPickerDelegate + + func documentPicker( + _ controller: UIDocumentPickerViewController, + didPickDocumentsAt urls: [URL] + ) { + guard + let url = urls.first, + url.startAccessingSecurityScopedResource() + else { + // TODO: Handle this error appropriately. + return + } + + var error: NSError? + + fileCoordinator.coordinate( + writingItemAt: url, + error: &error + ) { url in + fileManager.createFile( + atPath: url.appendingPathComponent(parent.fileName).relativePath, + contents: parent.fileData + ) + } + + url.stopAccessingSecurityScopedResource() + + parent.downloaded(error) + } + + func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { + controller.dismiss(animated: true) { [weak self] in + self?.parent.cancelled?() + } + } + + } +}