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()
}
}
}
}