skip to Main Content

I am using the new PHPickerViewController & Delegate on iOS15+.

I want my code to get get a callback from the picker (regardless of full photo access or limited) that the user has selected photo x, y, and z to attach to this chat. I’d like this callback regardless of the level of access given to my app to the photo library.

Here is how I show the photo picker:

            var configuration = PHPickerConfiguration(photoLibrary: .shared())
            // Set the filter type to images only for now
            configuration.filter = PHPickerFilter.any(of: [.images])
            // Set the mode to avoid transcoding, if possible, if your app supports arbitrary image/video encodings.
            configuration.preferredAssetRepresentationMode = .compatible
            // Set the selection behavior to respect the user’s selection order.
            configuration.selection = .ordered
            // Set the selection limit to enable multiselection.
            configuration.selectionLimit = 8
            // Set the preselected asset identifiers with the identifiers that the app tracks.
            configuration.preselectedAssetIdentifiers = self.selectedAssetIdentifiers
            // Show it
            let picker = PHPickerViewController(configuration: configuration)
            picker.delegate = self
            (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController?.present(picker, animated: true, completion: nil)

And here is my delegate:

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        dismiss(animated: true)
        let existingSelection = self.selection
        var newSelection = [String: PHPickerResult]()
        for result in results {
            let identifier = result.assetIdentifier!
            newSelection[identifier] = existingSelection[identifier] ?? result
        }
        
        // Track the selection in case the user deselects it later.
        _selection = newSelection
        selectedAssetIdentifiers = results.map(.assetIdentifier!)
        selectedAssetIdentifierIterator = selectedAssetIdentifiers.makeIterator()
        
        // Convert and load into the attachment manager
        let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: results.compactMap(.assetIdentifier), options: nil)
        // Loop through the objects in the fetch result using enumerateObjects
        fetchResult.enumerateObjects { (asset, index, stop) in
            // Do something with 'asset' and 'index'
            let options = PHImageRequestOptions()
            options.deliveryMode = .highQualityFormat
            options.resizeMode = .fast
            options.isNetworkAccessAllowed = true
            // Ideally we handle this better in the UI without freezing.
            options.isSynchronous = true
            
            let image = asset.image(targetSize: PHImageManagerMaximumSize, contentMode: PHImageContentMode.default, options: options)
            let handled = self.attachmentManager.handleInput(of: image)
            if !handled {
                // throw error
            }
        }
    }

In the limited photo mode, it seems like my didFinishPicking delegate method is never called—or at least never called in situations where the user has selected a photo from their library that they did not give me permission with the first time round.

Are there other methods I need to implement to support this privacy focused mode?


Here is the user flow generally, use case is a chat app where users can pick photos to attach to a message. The user selects ‘use only selected photos’, which then opens the picker to select which photos to give the app access to

privacy mode
permission picker

After that photo picker modal, nothing happens in privacy mode. No delegate methods are called, nothing. So my app now has access to certain photos—however, I’d like/need the user to further pick which photos, of the allowed photos, to attach to this specific chat.

If the user had selected full access, they would get a similar picker, but the delegate methods would fire and tell me which photos the user picked.

This is where my confusion is. I can’t find a definitive source for handling this case for what I expected to be a straight forward application of "User picks photos, I get photo in a callback so I can upload it to my backend"

2

Answers


  1. If you’re trying to avoid needing to ever prompt the user for Photo Library permissions then instead of using the asset identifiers to perform a PhotoKit fetch followed by PHImageManager requests then I’d suggest just getting the image data directly off the resulting PhotosPickerItems like what’s documented here: https://developer.apple.com/documentation/photokit/photospicker

    and here:
    https://developer.apple.com/documentation/photokit/bringing_photos_picker_to_your_swiftui_app

    By doing so, the user will never be shown a prompt asking to give your app access.

    If your app is in Limited Access mode already then the assets it has access to via PhotoKit are not extended to include assets selected with the PhotosPicker. That is done via the two presentLimitedLibraryPicker apis here: https://developer.apple.com/documentation/photokit/phphotolibrary/3616113-presentlimitedlibrarypicker

    Login or Signup to reply.
  2. I think there is some confusion in what you are trying to do. It sounds like you have a messaging app where you want to allow the user to select some photos to attach to a message. But your question and code seem to be confusing how PHPickerViewController works and how photo permissions work.

    If your app only uses PHPickerViewController to allow the user to select photos then your app will never prompt the user for any permission and the user won’t be able to restrict your app to only specific photos. The mere fact that a user selects a photo from the picker means they are implicitly granting your app permission to load that image. However, you need to load the image using the PHPickerResult given to you in the delegate method.

    If your app uses PHPhotoLibrary or PHAsset to fetch assets, then your app must obtain permission to access the library and the user may limit your app to only specific photos.

    The likely solution to your code is to update your delegate code to iterate the PHPickerResult array and use each result’s itemProvider property to get the UIImage of the selected photo.

    If needed you can keep track of the assetIdentifier of each picker result if you want to preselect the photos in the picker.

    By eliminating the use of PHAsset and the fetch calls, you don’t need any permissions or any of the photo related privacy strings in your Info.plist.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search