leonboe1
leonboe1

Reputation: 1185

SwiftUI 2 move file from Files app to documents directory with fileImporter

I'm currently trying to import (audio) files from the system Files app to the app documents directory using the new fileImporter.

Selecting files works well but apparently, my saveFile() function doesn't save the file to the documents directory since listing all files with listAllFiles() always returns an empty array. If I try to play a sound with the local (documents) URL it just doesn't work at all.

Update: Seems to be a permission issue. Getting the error message The file “XY.mp3” couldn’t be opened because you don’t have permission to view it. using try FileManager.default.copyItem(at: url, to: actualPath).

Update 2: Tried to do it with UIKit. Same issue as well.

Does somebody have an idea what I am doing wrong? Thanks a lot for your help!

import SwiftUI

struct ContentView: View {
    @State var importFiles = false
    @State var files = [URL]()
    
    var body: some View {
        VStack{
            Spacer()
            Button(action: {
                importFiles = true
            }, label: {Text("Import files")})
            .padding()
            
            Button(action: {
                listAllFiles()
            }, label: {Text("List files")})
            .padding()
            
            Spacer()
            ForEach(files, id: \.self){ file in
                Text(file.lastPathComponent)
                    .padding()
            }
        }
        .fileImporter(
            isPresented: $importFiles, allowedContentTypes: [.audio],
                allowsMultipleSelection: false
            ) { result in
                do {
                    let selectedFiles = try result.get()
                    if(selectedFiles.count == 1){
                        files.append(saveFile(url: selectedFiles[0]))
                    }
                    else{
                        print("error")
                    }
                    
                } catch {
                    // Handle failure.
                    print("failed")
                }
            }
    }
}

func getDocumentsDirectory() -> URL {
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}

func listAllFiles() {
    let documentsUrl = getDocumentsDirectory()

    do {
        let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil)
        print(directoryContents)

    } catch {
        print(error)
    }
}

func saveFile (url: URL) -> URL {
    
        let fileData = try? Data.init(contentsOf: url)
        let fileName = url.lastPathComponent
        
        let actualPath = getDocumentsDirectory().appendingPathComponent(fileName)
        do {
            try fileData?.write(to: actualPath, options: .atomic)
            print("success. origin: \(url) target: \(actualPath)")
        } catch {
            print("File could not be saved")
        }
    return actualPath
}

Upvotes: 2

Views: 1982

Answers (1)

leonboe1
leonboe1

Reputation: 1185

After doing a lot of research, I found out that including CFURLStartAccessingSecurityScopedResource and CFURLStopAccessingSecurityScopedResource to the saveFile function fixes the issue. However, I have absolutely no idea why. If anybody knows more, I'd be pleased to hear about it.

func saveFile (url: URL) {
    if (CFURLStartAccessingSecurityScopedResource(url as CFURL)) { // <- here
        
        let fileData = try? Data.init(contentsOf: url)
        let fileName = url.lastPathComponent
        
        let actualPath = getDocumentsDirectory().appendingPathComponent(fileName)
        do {
            try fileData?.write(to: actualPath)
            if(fileData == nil){
                print("Permission error!")
            }
            else {
                print("Success.")
            }
        } catch {
            print(error.localizedDescription)
        }
        CFURLStopAccessingSecurityScopedResource(url as CFURL) // <- and here
    }
    else {
        print("Permission error!")
    }
}

Upvotes: 4

Related Questions