skip to Main Content

I’m making Audio Player.
Importing file from iCloud Drive using .fileImporter.

I get file URL that looks like this: file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/_Storage/Audio-books/%D0%91%D1%80%D0%B5%D0%BD%D0%B4%D1%8F%D1%82%D0%B8%D0%BD%D0%B0/Audiobook.mp3"

Then I pass it to audio player (tried AVPlayer and AVAudioPlayer). Both works on iOS simulator.
On the device after import I get error: The operation couldn’t be completed. (OSStatus error -54.)

I know it’s possible, app called Evermusic does quite the same with on device files.

  • Is there permissions I need to be granted to play audio that stored on device?
  • How can I access Container for com~apple~CloudDocs?

Thank you very much for help, any suggestions greatly appreciated, I’m seriously stuck.
For future references repo of the project: https://github.com/yaosamo/AudioPlayer

2

Answers


  1. Chosen as BEST ANSWER

    In addition to @jnpdx answer want to add some details, and my solution example.

    Few core things

    ✅ for my app if you need to access secured audio you need to use startAccessingSecurityScopedResource()

    ❌ you can't simple store URL and use it later, in fact you don't store fileURL at all. You need to use what's called bookmarkData() on your URL and store it. So later you can restore URL

    Watch Apple pres here

    Here's how I import file:

            .fileImporter(isPresented: $presentImporter, allowedContentTypes: [.mp3]) { result in
            switch result {
            case .success(let url):
                // Start accessing secured url
                let StartAccess = url.startAccessingSecurityScopedResource()
                defer {
                    // Must stop accessing once stop using
                    if StartAccess {
                        url.stopAccessingSecurityScopedResource()
                    }
                }
                // Creating new book
                let newBook = Book(context: viewContext)
                let _ = print("---- Access Granted?", url.startAccessingSecurityScopedResource())
                // Getting bookmarkData of the URL
                let bookmarkData = try? url.bookmarkData()
                newBook.name = "(url.lastPathComponent)"
                // Save bookmarkURL into CoreData
                newBook.urldata = bookmarkData
                // Specifiying parent item in CoreData
                newBook.origin = playlist.self
                try? viewContext.save()
        
            case .failure(let error):
                print(error)
            }
        }
    

    Player retrieving URL:

    func Audioplayer(bookmarkData: Data) {
    
    // Restore security scoped bookmark
    var bookmarkDataIsStale = false
    let playNow = try? URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &bookmarkDataIsStale)
    
    do {
        player = try AVAudioPlayer(contentsOf: playNow!)
        // Delegate listen when audio is finished
        player?.delegate = del
        NotificationCenter.default.addObserver(forName: NSNotification.Name("ended"), object: nil, queue: .main) { (_) in
            player?.stop()
            ended = true
            let _ = print("---- Book has ended ----")
        }
    } catch let error {
        print("Player Error", error.localizedDescription)
    }
    player?.prepareToPlay()
    player?.play()
    }
    

    Thank you and again here's repo on git.


Please signup or login to give your own answer.
Back To Top
Search