1

I'm trying to let users choose a song from their music library. So, the problem: MPMediaPickerController displays a white screen and locks the app after a fresh install of the app and only after the user grants permission. Quitting the app and restarting it doesn't reproduce the error (perhaps because permissions have already been granted). Deleting the app and reinstalling reproduces the error.

  • I've made sure my Info.plist file has the correct information
  • I have made sure the media picker is being presented on the main thread
  • I have tried creating a property on my ViewController to hold a strong reference to the MPMediaPickerController instance to make sure it wasn't getting removed from memory (why not try?)
  • I have a valid Apple Music subscription and I'm signed into iCloud with a valid Apple ID
  • My device is registered on iTunes Connect (or whatever the name is) and has a valid provisioning profile
  • This is on iOS 13.1.3.

Here's how I initialize the picker (I've also tried by assigning a property on the view controller):

let picker = MPMediaPickerController(mediaTypes: .music)
picker.allowsPickingMultipleItems = false               // I've tried commenting this out
picker.popoverPresentationController?.sourceView = cell // I've tried commenting this out
picker.delegate = self                                  // I've tried commenting this out
picker.prompt = "Choose a song"                         // I've tried commenting this out
self.present(picker, animated: true, completion: nil)

Here's the relevant line in my Info.plist file:

<key>NSAppleMusicUsageDescription</key>
    <string>Use tracks from your iTunes library as wakeup sounds</string>

In the console, I get the following errors:

[MediaLibrary] SQLite error 14 detected while opening database '/var/mobile/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb'
[MediaLibrary] DISK IO ERROR: attempting to close and re-open connection for recovery.
[MediaLibrary] [_handleDiskIOError] checking database consistency
[Service] Failed to obtain service proxy to perform integrity check. err=Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 0 named com.apple.medialibraryd.xpc" UserInfo={NSDebugDescription=connection to service on pid 0 named com.apple.medialibraryd.xpc} There was an error waiting for a reply from the media library service. Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 0 named com.apple.medialibraryd.xpc" UserInfo={NSDebugDescription=connection to service on pid 0 named com.apple.medialibraryd.xpc}
[MediaLibrary] [_handleDiskIOError] failed to re-open database connection
[MediaLibrary] [_handleDiskIOError] FAILED TO HANDLE DISK IO ERROR
[MediaLibrary] [_handleDiskIOError] SHM file not found—unable to unlink
[MediaLibrary] [ML3DatabaseConnection] Unable to open database connection to path /var/mobile/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb. unable to open database file
[Service] Could not attempt recovery at path: /var/mobile/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb There was an error waiting for a reply from the media library service. Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 0 named com.apple.medialibraryd.xpc" UserInfo={NSDebugDescription=connection to service on pid 0 named com.apple.medialibraryd.xpc}
[xpc.exceptions] <NSXPCConnection: 0x281a5f3c0> connection to service on pid 466 named com.apple.Music.MediaPicker.viewservice: Exception caught during decoding of received selector remoteMediaPickerDidPickMediaItems:, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
<NSInvocation: 0x283203a00>
return value: {v} void
target: {@} 0x0
selector: {:} null
argument 2: {@} 0x0

Exception: Could not open database file at /var/mobile/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb (errno = 1)

My guess? Some race-condition sync issue with SQLite being in a different thread that's only a problem when waiting for the user to grant permissions. Is this even fixable on my end?

I'd like to not have a white screen of death! Any suggestions?

Josh Grant
  • 471
  • 4
  • 15
  • @matt, thanks for the comment. There is no code to check for permissions because that's taken care of by the system automatically after the user selects a song. I can't even ask for permissions even if I wanted to. – Josh Grant Oct 21 '19 at 11:46
  • No it isn’t automatically granted. That’s the problem right there. You can and must ask for permission first, and get it, before showing the picker. For the correct approach see https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch16p690mediaPicker/ch29p955mediaPicker/ViewController.swift – matt Oct 21 '19 at 11:51
  • @matt, I don't know where you're getting that information. I'd love a link. Apple says: Before your app can access certain protected resources, like the Bluetooth interface, location information, or the user’s photos, the system asks the user for permission on behalf of your app. To signal that your app needs the access, you add a UsageDescription key to your app’s Information Property List. You set the value associated with the key to a string that explains why your app needs access. The system displays this string when prompting the user, as described in Accessing Protected Resources. – Josh Grant Oct 21 '19 at 11:54
  • As I wrote in my post, I have a usage description key in my Info.plist file. Here's another snippet particular to the media picker: "Set the value of this key to a user-readable description of how you intend to use the user's media library. The first time your app access the user's media library, the system prompts the user to grant or deny authorization for your app to do so. The system includes this key's description in the dialog it displays to the user." – Josh Grant Oct 21 '19 at 11:56
  • 1
    Well what you're quoting is wrong. It used to be right but now it's wrong. You no longer get automatic permission on showing the picker. You have to ask for it first. Do you want to keep complaining or do you want to fix this? You said you don't want the white screen of death. This is how to prevent it. That's just the way it is. – matt Oct 21 '19 at 12:09

2 Answers2

4

It may be that in the past you could get away with presenting the media picker without first asking for and receiving authorization from the user, but in iOS 13 that is no longer the case. You must explicitly make sure you have authorization before presenting the media picker:

let status = MPMediaLibrary.authorizationStatus()
switch status {
case .authorized:
    // ok to present the picker
case .notDetermined:
    MPMediaLibrary.requestAuthorization() { status in
        if status == .authorized {
            // get on main thread, ok to present the picker
// ...

If you present the picker at a time when you do not already have authorization, it will freeze up as you have described.

You also need the Info.plist entry, as you have described, but that alone is not sufficient. You need both the Info.plist entry and the user's permission before you can present the picker. Without the former, you'll crash. Without the latter, you'll get the frozen white screen. With both, all will be well.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I'm doing this and can see I'm already authorized, however, I'm only seeing Apple Music items already added to the user library and nothing comes up when trying to browse for music on Apple Music. Is there some other authorization required to browse Apple Music songs not already in the library? – David L May 24 '20 at 16:04
  • I should clarify the above comment, I see the option to search on Apple Music, but I get a spinner that says loading but nothing is ever loaded. I have tried on 2 different Apple Music accounts and see the same behavior. – David L May 24 '20 at 16:11
  • Here are a couple screenshots showing what I see ([one](https://i.stack.imgur.com/cBC0S.png) and [two](https://i.stack.imgur.com/Ukd6X.png)). When the picker comes up I have 3 options: Library, For You and Browse. When I choose library I see my library when I choose the other 2, I get the continual spinner. It looks like I should be able to search Apple music for additional content not already in my library. – David L May 25 '20 at 13:34
  • Maybe you should ask a new question about all that. You would need to show how you are constructing and presenting the picker. Comments are not the place to iron that out. Basically you're asking a whole new question, so please _ask_ a whole new question. Thanks! – matt May 25 '20 at 13:50
  • I'm going to guess that the problem you describe is purely temporary. When I tap Join Apple Music, I get "Cannot connect to iTunes Store." So I suspect that Apple's server is down. – matt May 25 '20 at 13:57
  • Thanks, I'll see if the issue persists and ask a new question if needed. – David L May 25 '20 at 18:06
1

For me the selected answer wasn't the issue, although authorization and Info.plist are definitely needed. I originally had the set up of MPMediaPickerController in ViewDidLoad, and then presented the view controller within my IBAction function. Sometimes it would work, other times I got the blank screen.

After reviewing the Apple Documentation, all that code should actually be within the IBAction, including setting the delegate, or the delegate functions won't get called.

Displaying a Media Picker from Your App

@IBAction func selectSong(_ sender: UIButton) {

    let picker = MPMediaPickerController(mediaTypes: .music)
    picker.allowsPickingMultipleItems = false
    picker.popoverPresentationController?.sourceView = sender
    picker.delegate = self
    self.present(picker, animated: true, completion: nil)
}
Melly
  • 675
  • 5
  • 6