1

In my Xcode project, I have the following code set up (simplified code):

import Cocoa
import AVKit

class ViewController: NSViewController {

    var audioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            guard let filePath = Bundle.main.path(forResource: "sample", ofType: "mp3") else {
                print("ERROR: Failed to retrieve music file Path")
                return
            }
            let fileURL = URL.init(fileURLWithPath: filePath)
            print(" PATH: \(filePath)")
            print("  URL: \(fileURL)")
            try audioPlayer = AVAudioPlayer.init(contentsOf: fileURL)
        } catch {
            print("ERROR: Failed to retrieve music file URL")
        }

        audioPlayer.play() 
    }
}

//CONSOLE PRINTS ----------:
// PATH: /Users/vakho/Library/Developer/Xcode/DerivedData/MusicPlayer-foqzsckozmcjnofvlvhuwabfssqi/Build/Products/Debug/MusicPlayer.app/Contents/Resources/sample.mp3
//  URL: file:///Users/vakho/Library/Developer/Xcode/DerivedData/MusicPlayer-foqzsckozmcjnofvlvhuwabfssqi/Build/Products/Debug/MusicPlayer.app/Contents/Resources/sample.mp3

I am successfully able to pass the filePath of sample.mp3 (contained in application bundle) to audioPlayer by converting to URL first. Calling play() function plays the audio file.

However, since I am creating a music player app, I would like to be able to play an audio file that resides in directory folders, such as desktop, downloads folder, etc.... But when I attempt to pass a filePath of audio file outside app bundle, the code breaks:

import Cocoa
import AVKit

class ViewController: NSViewController {

    var audioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            let filePathOptional: String? = "/Users/vakho/Downloads/sample.mp3"
            guard let filePath = filePathOptional else {
                print("ERROR: Failed to retrieve music file Path")
                return
            }
            let fileURL = URL.init(fileURLWithPath: filePath)
            print(" PATH: \(filePath)")
            print("  URL: \(fileURL)")
            try audioPlayer = AVAudioPlayer.init(contentsOf: fileURL)
        } catch {
            print("ERROR: Failed to retrieve music file URL")
        }

        audioPlayer.play()
    }
}

//CONSOLE PRINTS ----------:
// PATH: /Users/vakho/Downloads/sample.mp3
//  URL: file:///Users/vakho/Downloads/sample.mp3
//ERROR: Failed to retrieve music file URL

//ERRORS ----------:
//  (lldb)
//  Thread 1: EXC_BAD_ACCESS (code=1, address=0x38)

From what I could conclude, AVAudioPlayer and AVKit library is designed for accessing app assets and bundle media files, as well as streaming media from the internet. But it fails to play media from directory.

I have found several threads about the issue, all incomplete or unanswered, such as: https://forums.developer.apple.com/thread/18272

So if AVKit cannot do what I thought it should have, what library or approach can I use to access directory files? Music player apps for OSX are of course able to prompt user to scan directory and access music files. How are they doing it?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Vakho
  • 87
  • 7

1 Answers1

2

Seems that problem is here:

guard let filePath = filePathOptional else {
    print("ERROR: Failed to retrieve music file Path")
    return
}

Please change the print to:

print("ERROR: Failed to retrieve music file Path \(error.localizedDescription)")

I think you'll see that the problem is that you have no access to this file. By default you have access only to sandbox of your app and Desktop folder.

Also try to put your file to Desktop and play it.

NikR
  • 628
  • 4
  • 14
  • I have tried putting the file on Desktop, but same thing happens. Then I tried formatting filePath as fileURL on my own according to how it was formatted in app bundle's case, and passed it to initializer. It seems that audioPlayer cannot be initialized with URL that's outside bundle, as I have speculated. If it truly is access permission issue, I will try requiring access. – Vakho Jun 11 '19 at 10:48
  • 2
    You we correct, I have caught the error message and printed it to console. It said "permErr: permissions error (on file open)". Then I read further into Apple documentation, and it turns out that Sanboxing is enabled for privacy reasons. Disabling Sanboxing is an option, but that means that you can no longer submit app to appstore. Proper solution was importing file/direcotry uisng NSOpenPanel. Thanks! – Vakho Jun 11 '19 at 12:25