Determine if the access to photo library is set or not - PHPhotoLibrary
Asked Answered
S

12

110

With the new functionality in iOS 8, if you are using a camera in the app, it will ask for permission to access the camera and then when you try to retake the pic, it asks for permission to access photo library. Next time when I launch the app, I wish to check if the camera and photo library has access permissions to it.

enter image description here

For camera, I check it by

if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusDenied)
{
// do something
}

I am looking for something similar to this for photo library.

Sagamore answered 27/10, 2014 at 19:27 Comment(0)
P
88

Check +[PHPhotoLibrary authorizationStatus] – if not set, it will return PHAuthorizationStatusNotDetermined. (You can then request access using +requestAuthorization: on the same class.)

Pervious answered 27/10, 2014 at 19:36 Comment(9)
Do I need to add/import any foundation or library to use PHPhotoLibrary? I am getting an error "Use of undeclared identifier"Sagamore
I tried using "ALAssetsLibrary" instead to check the authorization status and that returns YES even if the photo library is off.Sagamore
Oh am able to get the status using "ALAssetsLibrary". Still curios to know if it possible to use PHPhoto library.Sagamore
PHPhotoLibrary is part of the Photos framework, which is only available on iOS 8. If you need support for older versions of iOS, ALAssetsLibrary is probably your best bet.Pervious
Well as of iOS 9, ALAssetsLibrary is deprecated, so I guess thats why its not working.Chatoyant
You need to Enable Modules(C and Objective-C) in your projects build settings. This will allow you to use (@import Photos;) and declare a property of PHPhotoLibraryTussore
Do any of you have issue with checking that property again after it was changed in settings while the app was still running (not killed)? I get old status even with calling +requestAuthorization: Btw. tested with iOS 9.3Diapositive
Just import PhotosUI That's it!Triturate
Since iOS 8 we also have to provide usage description (which is required by PHPhotoLibrary). Here us the full answer: Here is a solution for iOS 8 and above: #26595843Smutchy
C
135

I know this has already been answered, but just to expand on @Tim answer, here is the code you need (iOS 8 and above):

PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];

if (status == PHAuthorizationStatusAuthorized) {
     // Access has been granted.
}

else if (status == PHAuthorizationStatusDenied) {
     // Access has been denied.
}

else if (status == PHAuthorizationStatusNotDetermined) {

     // Access has not been determined.
     [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {

         if (status == PHAuthorizationStatusAuthorized) {
             // Access has been granted.         
         }

         else {
             // Access has been denied.
         }
     }];  
}

else if (status == PHAuthorizationStatusRestricted) {
     // Restricted access - normally won't happen.
}

Don't forget to #import <Photos/Photos.h>

If you are using Swift 3.0 or higher, you can use the following code:

// Get the current authorization state.
let status = PHPhotoLibrary.authorizationStatus()

if (status == PHAuthorizationStatus.authorized) {
    // Access has been granted.
}

else if (status == PHAuthorizationStatus.denied) {
    // Access has been denied.
}

else if (status == PHAuthorizationStatus.notDetermined) {

    // Access has not been determined.
    PHPhotoLibrary.requestAuthorization({ (newStatus) in

        if (newStatus == PHAuthorizationStatus.authorized) {

        }

        else {

        }
    })
}

else if (status == PHAuthorizationStatus.restricted) {
    // Restricted access - normally won't happen.
}

Don't forget to import Photos

Chatoyant answered 7/10, 2015 at 9:46 Comment(6)
Why is this only iOS 9 and above? Photos framework has been available since iOS 8..Skittle
Also don't forget to add the Photos Framework in the Project -> Target -> Build PhasesOvertire
It doesn't work properly, i denied the access then enable it again, it still say notdetermined.Acceptable
"// Restricted access - normally won't happen." Why? It could happen: "The user cannot change this application’s status, possibly due to active restrictions"Tanhya
does PHPhotoLibrary.requestAuthorization suppose to show a dialog asking for permission? Cos right calling this line does not do anythingCardenas
I'd rather use switch case approach instead of if elseDinette
P
88

Check +[PHPhotoLibrary authorizationStatus] – if not set, it will return PHAuthorizationStatusNotDetermined. (You can then request access using +requestAuthorization: on the same class.)

Pervious answered 27/10, 2014 at 19:36 Comment(9)
Do I need to add/import any foundation or library to use PHPhotoLibrary? I am getting an error "Use of undeclared identifier"Sagamore
I tried using "ALAssetsLibrary" instead to check the authorization status and that returns YES even if the photo library is off.Sagamore
Oh am able to get the status using "ALAssetsLibrary". Still curios to know if it possible to use PHPhoto library.Sagamore
PHPhotoLibrary is part of the Photos framework, which is only available on iOS 8. If you need support for older versions of iOS, ALAssetsLibrary is probably your best bet.Pervious
Well as of iOS 9, ALAssetsLibrary is deprecated, so I guess thats why its not working.Chatoyant
You need to Enable Modules(C and Objective-C) in your projects build settings. This will allow you to use (@import Photos;) and declare a property of PHPhotoLibraryTussore
Do any of you have issue with checking that property again after it was changed in settings while the app was still running (not killed)? I get old status even with calling +requestAuthorization: Btw. tested with iOS 9.3Diapositive
Just import PhotosUI That's it!Triturate
Since iOS 8 we also have to provide usage description (which is required by PHPhotoLibrary). Here us the full answer: Here is a solution for iOS 8 and above: #26595843Smutchy
I
52

Just as formality, Swift 2.X version:

    func checkPhotoLibraryPermission() {
       let status = PHPhotoLibrary.authorizationStatus()
       switch status {
       case .Authorized:
            //handle authorized status
       case .Denied, .Restricted :
            //handle denied status
       case .NotDetermined:
            // ask for permissions
            PHPhotoLibrary.requestAuthorization() { (status) -> Void in
               switch status {
               case .Authorized:
                   // as above
               case .Denied, .Restricted:
                   // as above
               case .NotDetermined:
                   // won't happen but still
               }
            }
        }
    }

And Swift 3 / Swift 4:

    import Photos

    func checkPhotoLibraryPermission() {
        let status = PHPhotoLibrary.authorizationStatus()
        switch status {
        case .authorized: 
        //handle authorized status
        case .denied, .restricted : 
        //handle denied status
        case .notDetermined: 
            // ask for permissions
            PHPhotoLibrary.requestAuthorization { status in
                switch status {
                case .authorized: 
                // as above
                case .denied, .restricted: 
                // as above
                case .notDetermined: 
                // won't happen but still
                }
            }
        }
    }
Intone answered 1/3, 2016 at 4:18 Comment(1)
In Swift 3 don't forget to import Photos, if you want to use PHPhotoLibraryCacilie
S
29

Here is a complete guide for iOS 8+ (without ALAssetLibrary):

First, we have to provide the usage description as now it's required by PHPhotoLibrary.
For doing this we must open the file info.plist, find the key Privacy - Photo Library Usage Description and provide the value for it. If the key doesn't exist then just create it.
Here is an image for example:
enter image description here Also make sure that the value of the key Bundle name is not empty in the info.plist file.

Now when we have description, we can normally request authorization by calling requestAuthorization method:

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
    switch (status) {
        case PHAuthorizationStatusAuthorized:
            NSLog(@"PHAuthorizationStatusAuthorized");
            break;
        case PHAuthorizationStatusDenied:
            NSLog(@"PHAuthorizationStatusDenied");
            break;
        case PHAuthorizationStatusNotDetermined:
            NSLog(@"PHAuthorizationStatusNotDetermined");
            break;
        case PHAuthorizationStatusRestricted:
            NSLog(@"PHAuthorizationStatusRestricted");
            break;
    }
}];

NOTE 1: requestAuthorization actually doesn't show alert on every call. It shows once per some time, saves user's answer and returns it everytime instead of showing alert again. But as it isn't what we need, here is a useful code which always shows alert every time we need permission (with redirection to settings):

- (void)requestAuthorizationWithRedirectionToSettings {
    dispatch_async(dispatch_get_main_queue(), ^{
        PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
        if (status == PHAuthorizationStatusAuthorized)
        {
            //We have permission. Do whatever is needed
        }
        else
        {
            //No permission. Trying to normally request it
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                if (status != PHAuthorizationStatusAuthorized)
                {
                    //User don't give us permission. Showing alert with redirection to settings
                    //Getting description string from info.plist file
                    NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"];
                    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:@"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
                    
                    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
                    [alertController addAction:cancelAction];
                    
                    UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                    }];
                    [alertController addAction:settingsAction];
                    
                    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
                }
            }];
        }
    });
}

Common problem 1: Some users complain that the app doesn't show an alert after doing the above-mentioned changes in the info.plist file.
Solution: For testing try to change Bundle Identifier from the project file to something else, clean and rebuild the app. If it started working then everything is fine, rename it back.

Common Problem 2: There is some specific case when fetch results are not updated (and the views which used images from those fetch requests still empty accordingly) when the app gets permissions to photos, while running as it was promised in the documentation.
Actually it happens when we use WRONG code like this:

- (void)viewDidLoad {
    if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
    {
        //Reloading some view which needs photos
        [self reloadCollectionView];
        // ...
    } else {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if (status == PHAuthorizationStatusAuthorized)
                [self reloadCollectionView];
            // ...
        }];
    }
    // ...
}

In this case, if the user denied giving permissions on viewDidLoad then jumped to settings, allowed and jumped back to the the app, views will not be refreshed because [self reloadCollectionView] and fetch requests were not sent.
Solution: We just have to call [self reloadCollectionView] and do other fetch requests before requiring authorization like this:

- (void)viewDidLoad {
    //Reloading some view which needs photos
    [self reloadCollectionView];
    if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
    {
        // ...
}
Smutchy answered 15/7, 2016 at 11:29 Comment(1)
How do you link the settings of the app to permissions ?Hydromel
S
20

I did it like this:

- (void)requestPermissions:(GalleryPermissions)block
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];

    switch (status) 
    {
        case PHAuthorizationStatusAuthorized:
            block(YES);
            break;
        case PHAuthorizationStatusNotDetermined:
        {
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus)
            {
                if (authorizationStatus == PHAuthorizationStatusAuthorized)
                {
                    block(YES);
                }
                else
                {
                    block(NO);
                }
            }];
            break;
        }
        default:
            block(NO);
            break;
    }
}

And i send in what I need to do as the block depending on success or failure.

Staford answered 25/8, 2015 at 20:0 Comment(0)
V
14

iOS 14 onwards Apple has added a new feature that will give limited access to the photos library. Based on your requirements (Example creating custom photo gallery) you have to check if user has given limited access only and wants to give full access.

For backwards compatibility, the old versions without the parameter return .authorized even when you get limited access.

Swift 5:

switch PHPhotoLibrary.authorizationStatus(for: .readWrite) {
case .notDetermined:
    // ask for access
case .restricted, .denied:
    // sorry
case .authorized:
    // we have full access
 
// new option: 
case .limited:
    // we only got access to some photos of library
}

There is a code to call limited access screen again. if user has given .limited access only and you want user to select the images again.

PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: presentVCObj)

On every restart of app iOS will show alert to notify user about the limited access. If you want to stop that alert then add PHPhotoLibraryPreventAutomaticLimitedAccessAlert to YES in Info.plist

Veil answered 27/8, 2020 at 1:19 Comment(1)
This solution not working PHPhotoLibraryPreventAutomaticLimitedAccessAlert set to YES. Is there any other setting need to do after add this key in info.plist ?Leaky
H
8

UPDATE for: SWIFT 3 IOS10


Note: import Photos in AppDelegate.swift as follows

// AppDelegate.swift

import UIKit

import Photos

...


func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    photoLibraryAvailabilityCheck()

}

//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
    if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
    {

    }
    else
    {
        PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
    }
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
    if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
    {

    }
    else
    {
        alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
    }
}

//MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
func alertToEncourageCameraAccessWhenApplicationStarts()
{
    //Camera not available - Alert
    let internetUnavailableAlertController = UIAlertController (title: "Camera Unavailable", message: "Please check to see if it is disconnected or in use by another application", preferredStyle: .alert)

    let settingsAction = UIAlertAction(title: "Settings", style: .destructive) { (_) -> Void in
        let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
        if let url = settingsUrl {
            DispatchQueue.main.async {
                UIApplication.shared.open(url as URL, options: [:], completionHandler: nil) //(url as URL)
            }

        }
    }
    let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
    internetUnavailableAlertController .addAction(settingsAction)
    internetUnavailableAlertController .addAction(cancelAction)
    self.window?.rootViewController!.present(internetUnavailableAlertController , animated: true, completion: nil)
}
func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
{
    //Photo Library not available - Alert
    let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .alert)

    let settingsAction = UIAlertAction(title: "Settings", style: .destructive) { (_) -> Void in
        let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
        if let url = settingsUrl {
            UIApplication.shared.open(url as URL, options: [:], completionHandler: nil)
        }
    }
    let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
    cameraUnavailableAlertController .addAction(settingsAction)
    cameraUnavailableAlertController .addAction(cancelAction)
    self.window?.rootViewController!.present(cameraUnavailableAlertController , animated: true, completion: nil)
}

Answer updated from Alvin George

Harmsworth answered 15/11, 2016 at 20:10 Comment(0)
H
5

Using ALAssetsLibrary should work:

ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
switch (status) {
    case ALAuthorizationStatusNotDetermined: {
        // not determined
        break;
    }
    case ALAuthorizationStatusRestricted: {
        // restricted
        break;
    }
    case ALAuthorizationStatusDenied: {
        // denied
        break;
    }
    case ALAuthorizationStatusAuthorized: {
        // authorized
        break;
    }
    default: {
        break;
    }
}
Hypertension answered 10/6, 2015 at 9:15 Comment(1)
Great answer but this is deprecated in iOS 9.Chatoyant
S
4
I have a simple solution on swift 2.0

//
//  AppDelegate.swift
//  HoneyBadger
//
//  Created by fingent on 14/08/15.
//  Copyright (c) 2015 fingent. All rights reserved.
//

import UIKit
import Photos

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        self.window?.makeKeyAndVisible()

             self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginPageID")
            self.window?.rootViewController = initialViewController
            self.window?.makeKeyAndVisible()
        return true
    }
    func applicationDidEnterBackground(application: UIApplication) {
        print("Application On background", terminator: "")
    }
    func applicationDidBecomeActive(application: UIApplication) {
        cameraAllowsAccessToApplicationCheck()
        photoLibraryAvailabilityCheck()
    }
    //MARK:- CAMERA ACCESS CHECK
    func cameraAllowsAccessToApplicationCheck()
    {
        let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
        switch authorizationStatus {
        case .NotDetermined:
            // permission dialog not yet presented, request authorization
            AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo,
                completionHandler: { (granted:Bool) -> Void in
                    if granted {
                        print("access granted", terminator: "")
                    }
                    else {
                        print("access denied", terminator: "")
                    }
            })
        case .Authorized:
            print("Access authorized", terminator: "")
        case .Denied, .Restricted:
            alertToEncourageCameraAccessWhenApplicationStarts()
        default:
            print("DO NOTHING", terminator: "")
        }
    }
    //MARK:- PHOTO LIBRARY ACCESS CHECK
    func photoLibraryAvailabilityCheck()
    {
        if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
        {

        }
        else
        {
            PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
        }
    }
    func requestAuthorizationHandler(status: PHAuthorizationStatus)
    {
        if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
        {

        }
        else
        {
            alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
        }
    }

    //MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
    func alertToEncourageCameraAccessWhenApplicationStarts()
    {
        //Camera not available - Alert
        let internetUnavailableAlertController = UIAlertController (title: "Camera Unavailable", message: "Please check to see if it is disconnected or in use by another application", preferredStyle: .Alert)

        let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
            let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
            if let url = settingsUrl {
                dispatch_async(dispatch_get_main_queue()) {
                    UIApplication.sharedApplication().openURL(url)
                }

            }
        }
        let cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
        internetUnavailableAlertController .addAction(settingsAction)
        internetUnavailableAlertController .addAction(cancelAction)
        self.window?.rootViewController!.presentViewController(internetUnavailableAlertController , animated: true, completion: nil)
    }
    func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
    {
//Photo Library not available - Alert
        let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .Alert)

        let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
            let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
            if let url = settingsUrl {
                UIApplication.sharedApplication().openURL(url)
            }
        }
        let cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
        cameraUnavailableAlertController .addAction(settingsAction)
        cameraUnavailableAlertController .addAction(cancelAction)
        self.window?.rootViewController!.presentViewController(cameraUnavailableAlertController , animated: true, completion: nil)
    }
}
Surgeonfish answered 1/12, 2015 at 7:49 Comment(0)
R
1

Swift PHPhotoLibrary authorization

TL;DR

some of realisation for IPHONEOS_DEPLOYMENT_TARGET v14

import UIKit
import PhotosUI

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        self.requestPhotoLibraryAccess()
    }

    func requestPhotoLibraryAccess() {
        PHPhotoLibrary.requestAuthorization(for: .readWrite) { photoLibraryStatus in
            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                self.handlePhotoLibraryStatus(for: photoLibraryStatus)
            }
        }
    }

    func handlePhotoLibraryStatus(for status: PHAuthorizationStatus) {
        switch status {
        case .notDetermined:
            break
        case .authorized:
            break
        case .limited:
            self.showAlertForPhotoLibraryLimited()
        case .denied:
            self.showAlertForPhotoLibraryDenied()
        case .restricted:
            self.showAlertForPhotoLibraryRestricted()
        @unknown default:
            break
        }
    }

func showAlertForPhotoLibraryLimited() {
        let alert = UIAlertController(title: "Photo Library access is Limited", message: "Please Select more photos or Allow access to all photos in Settings", preferredStyle: .alert)
        
        let action2 = UIAlertAction(title: "Select more photos", style: .default) { [weak self] _ in
            guard let self = self else { return }
            PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
        }
        
        let action3 = UIAlertAction(title: "Keep Current Selection", style: .default)
        
        alert.addAction(self.getOpenSettingsAlertAction())
        alert.addAction(action2)
        alert.addAction(action3)
        
        self.present(alert, animated: true, completion: nil)
    
    }
    
    func showAlertForPhotoLibraryDenied() {
        let alert = UIAlertController(title: "Photo Library access is Denied", message: "Please provide access to PhotoLibrary to use the app", preferredStyle: .alert)
        alert.addAction(self.getOpenSettingsAlertAction())
        self.present(alert, animated: true, completion: nil)
    }
    
    func showAlertForPhotoLibraryRestricted() {
        let alert = UIAlertController(title: "Photo Library access is Restricted", message: "You are not able to grant such permission", preferredStyle: .alert)
        
        self.present(alert, animated: true, completion: nil)
    }
    
    func getOpenSettingsAlertAction() -> UIAlertAction {
        let action = UIAlertAction(title: "Open Settings", style: .default) { [weak self] _ in
            guard let self = self else { return }
            self.redirectToAppSettings()
        }
        
        return action
    }
        
    func redirectToAppSettings() {
        guard let url = URL(string: UIApplication.openSettingsURLString),
            UIApplication.shared.canOpenURL(url) else {
                print("not possible")
                return
        }

        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}

Explanation:

Provide description Info.plist with NSPhotoLibraryUsageDescription key which will be used in system's permission alert (inside message block) or you get next runtime error:

[access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data

Info.plist

<key>NSPhotoLibraryUsageDescription</key>
<string>photos_access_description</string>

This alert is shown once when one of PHPhotoLibrary function(e.g. PHPhotoLibrary.requestAuthorization(), PHPhotoLibrary.shared().register(), PHPhotoLibrary.authorizationStatus()...) is called and PHAuthorizationStatus is .notDetermined. When user made a decision and status now equal .notDetermined it is only one way to change it - using App's Settings

PHAuthorizationStatus:

.notDetermined - user has not make a choice. It is a default status before user set it explicitly via systems permission alert inside app.

.authorized - user authorised the full access to PhotoLibrary via Allow Access to All Photos or All Photos

.limited - from iOS v14. User authorised the limited access to PhotoLibrary. User is able to provide a set of items via Select Photos... or Selected Photos. After that application will operate only selected items and this set is unique for every application

  • This status is provided only by PHPhotoLibrary.authorizationStatus(for:) or PHPhotoLibrary.requestAuthorization(for:) which are available from iOS v14. It means if user set .limited access and you use PHPhotoLibrary.authorizationStatus or PHPhotoLibrary.requestAuthorization(even on iOS >= v14) you get .authorized status

  • Every new app lifecycle when status is .limited and a first access PhotoLibrary function is called (e.g PHAsset.fetchAssets()) system automatically show alert to adjust selected items. Sometimes it is not comfortable

    • To prevent this behaviour use next one in Info.plist:
    <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
    <string>YES</string>
    
    • To show Selection Picker manually use:
    PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: vc)
    
    • photoLibraryDidChange() is useful to control updates of selected items set. Please note that result of photoLibraryDidChange() should be called in main thread
    class MyViewController: UIViewController, PHPhotoLibraryChangeObserver {
        override func viewDidLoad() {
            super.viewDidLoad()
    
            PHPhotoLibrary.shared().register(self)
        }
    
        func photoLibraryDidChange(_ changeInstance: PHChange) {
            DispatchQueue.main.async { [weak self] in
                //logic
            }
        }
    }
    

    *I used import PhotosUI in other case I get Value of type 'PHPhotoLibrary' has no member 'presentLimitedLibraryPicker'

.denied - user denied access via Don't Allow or None

.restricted - system restricted the access, for example Parental Control. To reproduce it: Settings -> Screen Time -> <Turn On Screen Time> -> Content & Privacy Restrictions -> Photos -> Don't Allow Changes @unknown default - for backward compatibility

Open App Settings programatically:

func redirectToAppSettings() {
    guard let url = URL(string: UIApplication.openSettingsURLString),
        UIApplication.shared.canOpenURL(url) else {
            print("not possible")
            return
    }

    UIApplication.shared.open(url, options: [:], completionHandler: nil)
}

Please note that when user change this setting(status) in App's settings system automatically relaunches the app. But if status was not changed and it is important to control the status when user back to the app you can use NotificationCenter. For example:

override func viewDidLoad() {
    super.viewDidLoad()

    //self.requestPhotoLibraryAccess()
    NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)

@objc func didBecomeActive() {
    self.requestPhotoLibraryAccess()
}
}

Result of PHPhotoLibrary.requestAuthorization() should be called from main thread:

PHPhotoLibrary.requestAuthorization(for: .readWrite) { photoLibraryStatus in
    DispatchQueue.main.async { [weak self] in
        //logic
    }
}
Rhatany answered 22/8, 2023 at 19:10 Comment(0)
O
0

Here is a small and simple snippet that I usually use.

- (void)requestPhotoAuthorization:(void (^)(BOOL granted))granted
{
    void (^handler)(PHAuthorizationStatus) = ^(PHAuthorizationStatus status)
    {
        if (status == PHAuthorizationStatusAuthorized) granted(YES);
        else if (status == PHAuthorizationStatusNotDetermined) [PHPhotoLibrary requestAuthorization:handler];
        else granted(NO);
    };
    handler([PHPhotoLibrary authorizationStatus]);
}
Overanxious answered 19/8, 2015 at 16:40 Comment(2)
It doesn't seem to return granted(YES) or granted(NO) if it's undetermined?Staford
as above + capturing 'handler' strongly in this block is likely to lead to a retain cyclePatentor
R
0

Swift 2.0+

Based on a combination of answers here, I've created a solution for myself. This method only checks if there is no permission.

We got a method pickVideo() that requires access to photos. If it is not .Authorized ask for permission.

If permission is not given, pickVideo() will not be called, and the user cannot pick a video.

As long as the user did not give full access to photos, you can avoid to let them pick 'or crash' your application.

  // Method that requires access to photos
  func pickVideo(){
    // Check for permission
    if PHPhotoLibrary.authorizationStatus() != .Authorized{
      // If there is no permission for photos, ask for it
      PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
      return
    }
    //... pick video code here...
  }

  func requestAuthorizationHandler(status: PHAuthorizationStatus){
    if PHPhotoLibrary.authorizationStatus() == .Authorized{
      // The user did authorize, so, pickVideo may be opened
      // Ensure pickVideo is called from the main thread to avoid GUI problems
      dispatch_async(dispatch_get_main_queue()) {
        pickVideo()
      }
    } else {
      // Show Message to give permission in Settings
      let alertController = UIAlertController(title: "Error", message: "Enable photo permissions in settings", preferredStyle: .Alert)
      let settingsAction = UIAlertAction(title: "Settings", style: .Default) { (alertAction) in
        if let appSettings = NSURL(string: UIApplicationOpenSettingsURLString) {
          UIApplication.sharedApplication().openURL(appSettings)
        }
      }
      alertController.addAction(settingsAction)
      // If user cancels, do nothing, next time Pick Video is called, they will be asked again to give permission
      let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
      alertController.addAction(cancelAction)
      // Run GUI stuff on main thread
        dispatch_async(dispatch_get_main_queue()) {      
          self.presentViewController(alertController, animated: true, completion: nil)
        }
      }
    }
Rasbora answered 1/4, 2016 at 8:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.