HowTo —— File Import and Export in SwiftUI 2.0

Published on

Get weekly handpicked updates on Swift and SwiftUI!

Native file import and export functionality has been added to SwiftUI 2.0. It should be noted that the behavior of file export in different directories will vary, and the handling of permissions will also differ on different platforms.

Update

Currently, SwiftUI has made significant changes to the usage of file import and export.

fileImporter, fileExporter, and fileMover now correspond to import, export, and move operations, respectively.

An example is as follows:

Swift
  .fileImporter(isPresented: showImport, allowedContentTypes: [.zip], onCompletion: {
            result in
            switch result{
            case .success(let url):
                print(store.dataHandler.importData(url))
            case .failure(let error):
                print(error)
            }
            
            showImport.wrappedValue = false
  })

The system will automatically pop up a sheet. Currently, the fileImporter has a bug: if the sheet is canceled using a gesture, it will be difficult to bring it up again. It can only be canceled using the “cancel” function.

In fact, I prefer the previous usage, but the old usage has now been deprecated.


Original Article


importFiles

Swift
@Environment(\.importFiles) var importFile

importFile.callAsFunction(singleOfType: [.plainText]){ result in}

exportFiles

Swift
@Environment(\.exportFiles) var exportFile

try! exportFile.callAsFunction(FileWrapper(url: URL(fileURLWithPath:filePath), options: .immediate), contentType: .plainText){result in}

Sample Code

Swift
import SwiftUI

struct ExportImportTest: View {
    @Environment(\.importFiles) var importFile
    @Environment(\.exportFiles) var exportFile
    @State var text:String = ""
    var body: some View {
        List{
            Button("Generate File"){
                let filePath = NSTemporaryDirectory() + "test.txt"
                let outputText = "Hello World!"
                do {
                    try outputText.write(toFile: filePath, atomically: true, encoding: .utf8)
                    print("Test file has been generated")
                }
                catch let error {
                    print(error)
                }
            }
            
            Button("Import File importFiles"){
                importFile.callAsFunction(singleOfType: [.plainText]){ result in
                    switch result{
                    case .success(let url):
                        print(url)
                        do {
                            //iOS sandbox mechanism requires us to request temporary access to the url
                            _ = url.startAccessingSecurityScopedResource()
                            let fileData = try Data(contentsOf: url)
                            if let text = String(data:fileData,encoding: .utf8) {
                                self.text = text
                                print(text)
                            }
                            url.stopAccessingSecurityScopedResource()
                        }
                        catch let error {
                            print(error)
                        }
                    case .failure(let error):
                        print(error)
                    case .none:
                        break
                    }
                }
            }
            
            Button("Export File exportFiles"){
                //exportFile.callAsFunction(moving: URL, completion:  ) will move the file, and the source file will be deleted
                //If there is an error with the move (e.g. source file not found), the program will crash
                //When exporting a file from the temporary directory, whether using "move" or not, the source file will be deleted
                //Personally, I prefer the callAsFunction method of FileWrapper
                let filePath = NSTemporaryDirectory() + "test.txt"
                do {
                    try exportFile.callAsFunction(FileWrapper(url: URL(fileURLWithPath:filePath), options: .immediate), contentType: .plainText){result in
                        switch result{
                        case .success(let url):
                            print("File exported successfully: \(url)")
                        case .failure(let error):
                            print(error)
                        case .none:
                            break
                        }
                    }
                }
                catch let error {
                    print(error)
                }
            }
            
            Text("Imported File Content: \(text)")
        }
    }
}

For macOS, the App Sandbox - User Selected File setting in the project configuration needs to be set to read and write.

Regret

There is no native activityViewController provided.

Weekly Swift & SwiftUI insights, delivered every Monday night. Join developers worldwide.
Easy unsubscribe, zero spam guaranteed