Home > Back-end >  How to save an audio file using .fileimporter in SwiftUI and retrieve the files and show their filen
How to save an audio file using .fileimporter in SwiftUI and retrieve the files and show their filen

Time:12-29

I tried so hard to find a similar problem here but couldn't. So, how to save an audio file that has been imported using .fileimporter and save it in a folder in the documents directory. Also how to retrieve them and show their file names in a list in SwiftUI? I hope my question is clear enough. Here is the code that I tried.

import SwiftUI

extension FileManager {
    static func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
    
    static let customFolderURL = FileManager.getDocumentsDirectory().appendingPathComponent("Music Files")
}

class Manager: ObservableObject {
    @Published var musicFiles: [URL] = []
    
    init() {
        do {
            try FileManager.default.createDirectory(at: FileManager.customFolderURL, withIntermediateDirectories: true, attributes: nil)
        } catch {
            print("Failed to create folder")
        }
    }
    
    func save(url: URL) {
        if url.startAccessingSecurityScopedResource() {
            do {
                let musicFile = try Data(contentsOf: url)
                try musicFile.write(to: FileManager.customFolderURL, options: .atomic)
                print("Success")
                print(FileManager.customFolderURL)
            } catch {
                print("Failed to save the music file")
            }
            url.stopAccessingSecurityScopedResource()
        } else {
            print("Permission failed!")
        }
    }
    
    func getMusicFiles() {
        do {
            let musicFiles = try FileManager.default.contentsOfDirectory(atPath: FileManager.customFolderURL.path)
            print(musicFiles)
        } catch {
            print("Failed to retrieve items.")
        }
    }
}

struct MainView: View {
    @State private var isFileImporterShown = false
    @State private var isMusicPlayerShown = false
    
    @EnvironmentObject var manager: Manager
    
    var body: some View {
        TabView {
            VStack {
                Button("Import Song") {
                    isFileImporterShown.toggle()
                }
                .padding()
                .fileImporter(isPresented: $isFileImporterShown, allowedContentTypes: [.audio], allowsMultipleSelection: true) { result in
                    do {
                        let url = try result.get().first!
                        if url.startAccessingSecurityScopedResource() {
                            manager.save(url: url)
                        }
                    } catch {
                        
                    }
                }
                
                Button("Show Player") {
                    isMusicPlayerShown.toggle()
                }
                .padding()
                .sheet(isPresented: $isMusicPlayerShown) {
                    MusicPlayerView()
                }
            }
            .tabItem {
                Image(systemName: "externaldrive")
                Text("Load")
            }
            
            AllSongsListView()
                .tabItem {
                    Image(systemName: "music.note.list")
                    Text("Songs")
                }
        }
    }
}

struct AllSongsListView: View {
    @EnvironmentObject var manager: Manager
    var body: some View {
        NavigationView {
            List {
                
            }
            .navigationTitle("Your Songs")
            .onAppear {
                manager.getMusicFiles()
            }
        }
    }
}

CodePudding user response:

The problem with saving is that you're trying to save the file as a directory.

try musicFile.write(to: FileManager.customFolderURL, options: .atomic)

So the to argument is a folder path and not a file path.

You need to change it like this:

let savePath = FileManager.customFolderURL.appendingPathComponent(url.lastPathComponent)
                
try musicFile.write(to: savePath, options: .atomic)

And for displaying the list of imported files you can do something like:

List {
        ForEach(manager.musicFiles, id: \.self) { file in
            Text(file.lastPathComponent)
        }
}

Here is the complete example:

import SwiftUI

extension FileManager {
    static func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
    
    static let customFolderURL = FileManager.getDocumentsDirectory().appendingPathComponent("Music Files")
}

class Manager: ObservableObject {
    @Published var musicFiles: [URL] = []
    
    static var shared = Manager()
    
    init() {
        do {
            try FileManager.default.createDirectory(at: FileManager.customFolderURL, withIntermediateDirectories: true, attributes: nil)
        } catch {
            print("Failed to create folder:")
            print(error)
        }
    }
    
    func save(url: URL) {
        if url.startAccessingSecurityScopedResource() {
            do {
                let musicFile = try Data(contentsOf: url)
                let savePath = FileManager.customFolderURL.appendingPathComponent(url.lastPathComponent)
                
                try musicFile.write(to: savePath, options: .atomic)
                print("Success")
                musicFiles.append(savePath)
            } catch {
                print("Failed to save the music file:")
                print(error)
            }
            url.stopAccessingSecurityScopedResource()
        } else {
            print("Permission failed!")
        }
    }
    
    func getMusicFiles() {
        do {
            let musicFiles = try FileManager.default.contentsOfDirectory(atPath: FileManager.customFolderURL.path)
            print(musicFiles)
        } catch {
            print("Failed to retrieve items.")
        }
    }
}

struct MainView: View {
    @State private var isFileImporterShown = false
    @State private var isMusicPlayerShown = false
    
    @ObservedObject var manager = Manager.shared
    
    var body: some View {
        TabView {
            VStack {
                Button("Import Song") {
                    isFileImporterShown.toggle()
                }
                .padding()
                .fileImporter(isPresented: $isFileImporterShown, allowedContentTypes: [.audio], allowsMultipleSelection: true) { result in
                    do {
                        let url = try result.get().first!
                        if url.startAccessingSecurityScopedResource() {
                            manager.save(url: url)
                        }
                    } catch {
                        print(error)
                    }
                }
                
                Button("Show Player") {
                    isMusicPlayerShown.toggle()
                }
                .padding()
                .sheet(isPresented: $isMusicPlayerShown) {
                    Text("Musiker")
                }
            }
            .tabItem {
                Image(systemName: "externaldrive")
                Text("Load")
            }
            
            AllSongsListView()
                .tabItem {
                    Image(systemName: "music.note.list")
                    Text("Songs")
                }
        }
    }
}

struct AllSongsListView: View {
    @ObservedObject var manager = Manager.shared
    
    var body: some View {
        NavigationView {
            List {
                ForEach(manager.musicFiles, id: \.self) { file in
                    Text(file.lastPathComponent)
                }
            }
            .navigationTitle("Your Songs")
            .onAppear {
                manager.getMusicFiles()
            }
        }
    }
}
  • Related