PHPickerViewController 是系統內建的一個照片選取工具,透過 PHPickerConfiguration 設定它,然後從它的 delegate function func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) 取得用戶選取的結果。看起來非常簡單直覺,直到我踩了坑...

我的目標是要從回傳的 [PHPickerResult] 轉成 [PHAsset],首先要取得所有的 assetIdentifier,接下來使用 PHAssetfetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAsset> 得到 fetch results。

PHPickerResultassetIdentifier 總是為 nil

PHPickerViewControllerDelegate 我們可以得到 results: [PHPickerResult],然後就能將它轉成 assetIdentifier array

let identifiers = results.compactMap { $0.assetIdentifier }

但我發現 identifiers 總是空的...

研究了好久之後才發現問題出在 PHPickerConfiguration

當我們要初始化它的時候,不能直接使用 let config = PHPickerConfiguration(),如果沒有提供 photo library 參數給它,assetIdentifier 就會總是為 nil。因此正確的做法如下:

let configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
self.present(picker, animated: true, completion: nil)

PHFetchResultPHPickerResult 數量不一致?

好不容易取得正確的 identifiers,接下來就是要透過 identifiers 取得 PHAsset 了。我們可以這麼做:

let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers:identifiers, options:nil)
fetchResult.enumerateObjects() { asset, _, _ in
    // Deal with the asset
}

但我發現 fetchResult.countidentifiers.count 並非總是一致,可取得的 assets 可能會比 identifiers 少。明明我們確定 identifiers 都是正確且合法的,為什麼無法取得所有對應的 PHAsset 呢?

經過一番排查之後,我才發現這跟照片的存取權限有關:

如果使用者有限制 app 只能存取特定照片,即使 PHPickerViewController 可以讓使用者看到並選取任何照片,但實際可以取得的只有使用者允許的部分。

以上,就是我的踩坑之路。