iosswiftmpmediapickercontroller

MPMediaPickerController blank screen, various errors, proper Info.plist file (iOS 13.1.3)


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.

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?


Solution

  • 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.