0

I'm attempting to write an IOS app that will play my iPhone music library content, showing artwork, and "announcing" title, artist, etc.

I have it working nicely using Apple's Media Player framework. I can display Playlist names and queue the songs in a selected Playlist. I use the "MPMusicPlayerControllerNowPlayingItemDidChange" observer notification to pause playback, retrieve metadata, and do the announcements via AVSpeechSynthesizer. I was a happy camper until I ran into the dreaded "Media Player framwork doesn't respond to observer notifications in background" issue.

So, I started looking at the AVFoundation Framework. I found a sample that plays local song files via URLs in the background and. I'm failing miserably in attempting to retrieve Music Library content via the AVFoundation. I have also failed in supplying content retrieved via the Media Player framework to the AVFoundation player. (Note: The URLs retrieved from MPMediaItem are of a bogus "ipod-library://item/item.m4a?id=#########################" format. Creating AVPlayerItem with this "URL" doesn't work.)

Has anyone managed to accomplish this? I'm developing for my own usage. I have no intention of posting the app in Apple's App Store, so I'm willing to use hidden APIs or un-Apple approved methodology.

A Swift code example would be great. (Objective-C not so much)

matt
  • 515,959
  • 87
  • 875
  • 1,141
Uncle Bill
  • 53
  • 7

1 Answers1

0

Having fetched an MPMediaItem from the user's library, obtain its assetURL. Creating an AVPlayer from the resulting URL does work.

Actual code from one of my example apps:

func oneSong () -> (URL?, String?) {
    let query = MPMediaQuery.songs()
    // always need to filter out songs that aren't present
    let isPresent = MPMediaPropertyPredicate(value:false,
        forProperty:MPMediaItemPropertyIsCloudItem,
        comparisonType:.equalTo)
    query.addFilterPredicate(isPresent)
    let item = query.items?[0]
    return (item?.assetURL, item?.title)
}
@IBAction func doPlayOneSongAVPlayer (_ sender: Any) {
    let (url, title) = self.oneSong()
    if let url = url, let title = title {
        self.avplayer = AVPlayer(url:url)
        self.avplayer.play()
        MPNowPlayingInfoCenter.default().nowPlayingInfo = [
            MPMediaItemPropertyTitle : title
        ]
    }
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks. I was using creating an array of AVPlayerItems from the MPMediaItems URLs and populating the AVQueuePlayer.....no joy. Your suggestion works great. – Uncle Bill Feb 28 '19 at 17:37
  • Done. Am I correct that AVAudioPlayer does not work in the Simulator? It works when "sideloaded" (I think that is the correct term for deploying on a physical device via Xcode.) MPMediaPlayer worked in the Simulator. – Uncle Bill Mar 01 '19 at 15:48
  • As far as I know, AVAudioPlayer works fine in the Simulator, and so does AVPlayer (used in my example). I am surprised to hear that MPMediaPlayer works for you in the Simulator, however, because there is no way to get playable media onto the Simulator to begin with. For that reason, the device is the only place I've ever actually run that code. – matt Mar 01 '19 at 16:00
  • Not to be argumentative, but your comment about not being able to add music to the simulator is incorrect. I've done it following the stackoverflow post [link](https://stackoverflow.com/questions/41300608/can-i-add-a-music-library-to-the-xcode-ios-simulator). I expanded it and wrote an applescript that uses softlinks to provide music to all device simulators. I posted the code to the above link, but it was rejected as "not answering question". – Uncle Bill Mar 02 '19 at 19:25
  • I see. Too hacky for my blood, however. – matt Mar 02 '19 at 19:32