I followed the Swift project UsingFiles github and its video to write files in my SwiftUI project. The project UsingFiles can write files to Files APP and then the writing files
As I have already posted in comments you can NOT programmatically save a file out of your APP Bundle. You can use a UIDocumentInteractionController
and ask the user to choose the location where the file is supposed to be written.
So if you are working with SwiftUI this gets a bit more complicated than the regular Storyboard approach as you can see in this post because you need to implement UIViewControllerRepresentable
for UIDocumentInteractionController
:
struct DocumentInteractionController: UIViewControllerRepresentable {
fileprivate var isExportingDocument: Binding<Bool>
fileprivate let viewController = UIViewController()
fileprivate let documentInteractionController: UIDocumentInteractionController
init(_ isExportingDocument: Binding<Bool>, url: URL) {
self.isExportingDocument = isExportingDocument
documentInteractionController = .init(url: url)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentInteractionController>) -> UIViewController { viewController }
func updateUIViewController(_ controller: UIViewController, context: UIViewControllerRepresentableContext<DocumentInteractionController>) {
if isExportingDocument.wrappedValue && documentInteractionController.delegate == nil {
documentInteractionController.uti = documentInteractionController.url?.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = documentInteractionController.url?.localizedName
documentInteractionController.presentOptionsMenu(from: controller.view.frame, in: controller.view, animated: true)
documentInteractionController.delegate = context.coordinator
documentInteractionController.presentPreview(animated: true)
}
}
func makeCoordinator() -> Coordintor { .init(self) }
}
And its Coordinator:
class Coordintor: NSObject, UIDocumentInteractionControllerDelegate {
let documentInteractionController: DocumentInteractionController
init(_ controller: DocumentInteractionController) {
documentInteractionController = controller
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController { documentInteractionController.viewController }
func documentInteractionControllerDidDismissOptionsMenu(_ controller: UIDocumentInteractionController) {
controller.delegate = nil
documentInteractionController.isExportingDocument.wrappedValue = false
}
}
Now you can create your DocumentInteraction View and its previews:
struct DocumentInteraction: View {
@State private var isExportingDocument = false
var body: some View {
VStack {
Button("Export Document") { self.isExportingDocument = true }
.background(DocumentInteractionController($isExportingDocument,
url: Bundle.main.url(forResource: "sample", withExtension: "pdf")!))
}
}
}
struct DocumentInteraction_Previews: PreviewProvider {
static var previews: some View { DocumentInteraction() }
}
You will need those helpers as well:
extension URL {
var typeIdentifier: String? { (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier }
var localizedName: String? { (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName }
}
Sample project
I used UIActivityViewController to save a file on my phone. It's simple and works.
import SwiftUI
struct ContentView: View {
var body: some View {
Button(action: wirtieFile) {
Image(systemName: "square.and.arrow.up")
}
}
func wirtieFile() -> Void{
let file = "test.txt"
let dir = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(file)
let contents = "test..."
do {
try contents.write(to: dir!, atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}
var filesToShare = [Any]()
filesToShare.append(dir!)
let av = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
}
}