Currently, there must be a request for access to documents with xcode, but we do not request access.
Added NSDocumentDirectoryUsageDescription to info.
import SwiftUI import AVFoundation import UniformTypeIdentifiers
struct ContentView: View { @State private var audioPlayer: AVAudioPlayer? @State private var isPlaying = false @State private var currentFileName: String = "No file selected" @State private var showingDocumentPicker = false @State private var songList: [String] = UserDefaults.standard.stringArray(forKey: "SavedSongs") ?? [] @State private var currentSongIndex: Int = 0
var body: some View {
VStack {
Text(currentFileName)
.font(.headline)
.padding()
List {
ForEach(songList, id: \.self) { song in
HStack {
Text(song)
.onTapGesture {
loadAndPlaySong(named: song)
}
Spacer()
Button(action: {
removeSong(named: song)
}) {
Image(systemName: "trash")
.foregroundColor(.red)
}
}
}
}
.listStyle(PlainListStyle())
HStack(spacing: 10) {
Button(action: {
showingDocumentPicker = true
}) {
Text("Load")
.font(.system(size: 12))
.frame(width: 50, height: 25)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(5)
}
.padding(.bottom, 20)
}
.padding()
.sheet(isPresented: $showingDocumentPicker) {
DocumentPicker(audioPlayer: $audioPlayer, currentFileName: $currentFileName, songList: $songList)
}
.onAppear {
if !songList.isEmpty {
loadAndPlaySong(named: songList[currentSongIndex])
}
}
}
func playSong() {
audioPlayer?.play()
isPlaying = true
}
func pauseSong() {
audioPlayer?.pause()
isPlaying = false
}
func stopSong() {
audioPlayer?.stop()
audioPlayer?.currentTime = 0
isPlaying = false
}
func nextSong() {
currentSongIndex = (currentSongIndex + 1) % songList.count
loadAndPlaySong(named: songList[currentSongIndex])
}
func previousSong() {
currentSongIndex = (currentSongIndex - 1 + songList.count) % songList.count
loadAndPlaySong(named: songList[currentSongIndex])
}
func loadAndPlaySong(named songName: String) {
guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentsURL.appendingPathComponent(songName)
do {
audioPlayer = try AVAudioPlayer(contentsOf: fileURL)
audioPlayer?.prepareToPlay()
currentFileName = songName
playSong()
} catch {
print("Error loading file: \(error.localizedDescription)")
}
}
func removeSong(named songName: String) {
songList.removeAll { $0 == songName }
UserDefaults.standard.set(songList, forKey: "SavedSongs")
}
}
struct DocumentPicker: UIViewControllerRepresentable { @Binding var audioPlayer: AVAudioPlayer? @Binding var currentFileName: String @Binding var songList: [String]
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.audio])
documentPicker.delegate = context.coordinator
return documentPicker
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
class Coordinator: NSObject, UIDocumentPickerDelegate {
let parent: DocumentPicker
init(_ parent: DocumentPicker) {
self.parent = parent
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else { return }
do {
if url.startAccessingSecurityScopedResource() {
defer { url.stopAccessingSecurityScopedResource() }
let fileName = url.lastPathComponent
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationURL = documentsURL.appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: destinationURL.path) {
try FileManager.default.removeItem(at: destinationURL)
}
try FileManager.default.copyItem(at: url, to: destinationURL)
parent.audioPlayer = try AVAudioPlayer(contentsOf: destinationURL)
parent.audioPlayer?.prepareToPlay()
parent.currentFileName = fileName
if !parent.songList.contains(fileName) {
parent.songList.append(fileName)
UserDefaults.standard.set(parent.songList, forKey: "SavedSongs")
}
} else {
print("I can't get file access.")
}
} catch {
print("An error occurred while loading the file: \(error.localizedDescription)")
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
}
}
}