8

iOS 10 now requires the user's permission to access the Media Library. We check if we have access to the Media Library before we use it, and if not we then use [MPMediaLibrary requestAuthorization: to request authorization again from the user.

I'm expecting this to show the same popup request to access the Media Library that we get at app startup, but nothing happens. It is simply returning with the MPMediaLibraryAuthorizationStatusDenied status from before.

The docs for requestAuthorization are incomplete at this time, so I can't tell if I'm just using this incorrectly, or there is something else wrong.

    if ( MPMediaLibrary.authorizationStatus == MPMediaLibraryAuthorizationStatusAuthorized)
    {
        // we already have access to the Media Library - use it here...
    }
    else
    {
        // We expect this to show a popup so the user can grant access, but does not work
        [MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus authorizationStatus)
         {
             if ( authorizationStatus == MPMediaLibraryAuthorizationStatusAuthorized )
             {
                 // success: the user authorized - use it here...
             }
             else
             {
                 // user did not authorize - tell user why here...
             }
         }];
    }

Update

Apparently there is no way to cause the original dialog to reappear (see comments below). I'm now using this code to at least take me to the right place in settings so the user can make the change. (for iOS8 and beyond)

NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:url];
Jim Leask
  • 6,159
  • 5
  • 21
  • 31
  • Denied is normally different from "not determined" which you should get. Did you not deny access the first time you ran the application? You'll have to reset this if it's the case (either in Settings or by deleting then reinstalling the app)? – jcaron Sep 07 '16 at 17:00
  • Yes, we denied access when the initial dialog was presented when the app initially started. However, what is the point of the `MPMediaLibrary requestAuthorization` call if it doesn't cause this prompt to reappear. Otherwise it is the same as simply doing `MPMediaLibrary.authorizationStatus` and reporting back the existing status. That isn't what we need. – Jim Leask Sep 07 '16 at 19:31
  • All iOS authorisation dialogs I know of work the same: they ask once, and remember what the user selected. They are never shown again for a given app unless it's uninstalled. The only way to change the selection after that is through the Settings app. The point of the call is to show the dialog the first time. – jcaron Sep 07 '16 at 20:26
  • OK. We found the call to bring up the settings so at least we can bring the user to the right place to make the change. However, the initial dialog comes up without our interaction, so I'm still not clear on why they even bother with the `requestAuthorization` API, as it won't do anything different than the simple query for the existing status. I'll update the question with the code to call the settings. – Jim Leask Sep 07 '16 at 20:47
  • It probably comes because you accessed the media library before asking for authorisation explicitly. This varies between frameworks: some will just return an error if you do, others will prompt the user. You could refrain from any access, and call requestAuthorization at a time that makes sense for you (e.g. in an initial "guided tour"). – jcaron Sep 07 '16 at 22:17
  • Ah, that is possible. Thanks for the tip. If you put this into an answer, I'd mark it as answered as I think you are correct. We are writing code to expect this behaviour that I'll add as an answer as well for reference. – Jim Leask Sep 08 '16 at 13:23

1 Answers1

8

The MPMediaLibrary will only automatically prompt the user once. The state is MPMediaLibraryAuthorizationStatusNotDetermined if you ask for it before it has been granted or denied by the user. If they have denied access previously, you need to send the user to the System Settings so they can manually turn it on for your app.

The following code is how we are doing it.

+ (void) validateMediaLibraryForMinimumIosAndAboveWithViewController:(UIViewController *)viewController
                                                        ifAuthorized:(void(^)())authorizedBlock
                                                     ifNotAuthorized:(void(^)())notAuthorizedBlock
{
    MPMediaLibraryAuthorizationStatus authorizationStatus = MPMediaLibrary.authorizationStatus;

    switch (authorizationStatus)
    {
        case MPMediaLibraryAuthorizationStatusAuthorized:
        {
            // We are already authorized - proceed
            if( authorizedBlock )
            {
                authorizedBlock();
            }
            break;
        }
        case MPMediaLibraryAuthorizationStatusNotDetermined:
        {
            // Not yet authorized - request it from the system
            [MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus authorizationStatus)
             {
                 if ( authorizationStatus == MPMediaLibraryAuthorizationStatusAuthorized )
                 {
                     if( authorizedBlock )
                     {
                         authorizedBlock();
                     }
                 }
                 else
                 {
                     PLog(@"The Media Library was not authorized by the user");
                     if( notAuthorizedBlock )
                     {
                         notAuthorizedBlock();
                     }
                 }
             }];
            break;
        }

        case MPMediaLibraryAuthorizationStatusRestricted:
        case MPMediaLibraryAuthorizationStatusDenied:
        {
            // user has previously denied access. Ask again with our own alert that is similar to the system alert
            // then take them to the System Settings so they can turn it on for the app
            NSString *titleString  = NSLocalizedStringWithDefaultValue(@"Media Library Privacy Alert Title",
                                                                       @"Localizable",
                                                                       [NSBundle mainBundle],
                                                                       @"Would Like to Access Apple Music And Your Media Library",
                                                                       @"Title for dialog requesting media library access");

            [self displayPermissionAlertFromViewController:viewController
                                                 withTitle:titleString];
            if( notAuthorizedBlock )
            {
                notAuthorizedBlock();
            }
            break;
        }
    }
}

+ (void)displayPermissionAlertFromViewController:(UIViewController *)viewController withTitle:(NSString *)title
{
    NSString* appName = [[NSProcessInfo processInfo] processName];

    NSString *titleString = [NSString stringWithFormat:@"\"%@\" %@",appName, title];

    NSString *cancelString = NSLocalizedStringWithDefaultValue(@"Don't Allow",
                                                               @"Localizable",
                                                               [NSBundle mainBundle],
                                                               @"Don't Allow",
                                                               @"Don't allow button");

    NSString *settingsString = NSLocalizedStringWithDefaultValue( @"Settings",
                                                                 @"Localizable",
                                                                 [NSBundle mainBundle],
                                                                 @"Settings",
                                                                 @"Settings button label");

    UIAlertController *alertController = [UIAlertController
                                          alertControllerWithTitle:titleString
                                          message:nil
                                          preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:[UIAlertAction actionWithTitle:cancelString
                                                        style:UIAlertActionStyleDefault
                                                      handler:nil]];

    [alertController addAction:[UIAlertAction actionWithTitle:settingsString
                                                        style:UIAlertActionStyleDefault
                                                      handler:
                                ^(UIAlertAction * _Nonnull action)
                                {
                                    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                                    [[UIApplication sharedApplication] openURL:url];
                                }]];

    [viewController presentViewController:alertController animated:true completion:nil];
}
Jim Leask
  • 6,159
  • 5
  • 21
  • 31