Recently, started noticing some assets were not being displayed when importing them from the photo library. The assets in questions are stored in iCloud and are not cached on the device. I believe it is an iOS 14 issue, since I have never experienced this issue on iOS 13. (I am not 100% sure since I am not able to roll out my personal device to iOS 13).
Here is what I am doing in iOS 14:
- Using the new picker view controller to import video assets
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = PHPickerFilter.videos
let videoPickerController = PHPickerViewController(configuration: configuration)
videoPickerController.delegate = self
present(videoPickerController, animated: true, completion: nil)
- I extract the PHAsset from the [PHPickerResult] (delegate method)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard results.count > 0 else {
return
}
guard let firstPHAssetIdentifier = results.first?.assetIdentifier else {
fatalError("No asset identifier")
}
let fetchOptions = PHFetchOptions()
guard let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [firstPHAssetIdentifier], options: fetchOptions).firstObject else {
fatalError("No matching PHAsset")
}
guard phAsset.mediaType == .video else {
fatalError("Asset not of the video type")
}
}
- Then, I request an AVAsset for the matching PHAsset
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
options.progressHandler = { progress, _, _, _ in
print("Progress: \(progress)")
}
PHCachingImageManager.default().requestAVAsset(forVideo: phAsset, options: options) { [weak self] avAsset, _, info in
guard info?[PHImageCancelledKey] == nil && info?[PHImageErrorKey] == nil else {
print("Error or cancelled. Info: \(String(describing: info))")
return
}
guard let avAsset = avAsset else {
print("Asset is nil. Info: \(String(describing: info))")
return
}
guard let videoTrack = avAsset.tracks(withMediaType: .video).first else {
print("Cound not extract video track from AvAsset") // <- My issue
return
}
}
Problem: Often, I won't have a videoTrack when the asset is coming from iCloud. The avAsset.duration will also be 0.0.
I will see the download progress but I will fall in that last guard statement. Sometimes, once the asset has been downloaded and could not load videoTrack, retrying will just instantly fail (it will not try to load the asset again, seems like it's corrupted). It will fall into that last guard statement.
I noticed using deliveryMode = .highQualityFormat on the PHVideoRequestOptions makes it work but I would rather download only a 720p video, not a high quality video.
I suppose I am doing something wrong here. Should I get the phAsset from the PHPickerResult this way? Any pointer / help would be greatly appreciated. I created this repo to repro https://github.com/SwiftRabbit/RequestAVAssetIssues/tree/main/RequestAVAssetIssues. Doesn't repro 100% and has to be iCloud videos that are not on device anymore.
Additional notes / attempts:
- I have experienced the same issue with PHCachingImageManager and PHImageManager
- Some popular apps seem to have the same issue for some assets (e.g. Instagram, TikTok...)
- PHCachingImageManager.default().requestPlayerItem does not work either. It returns an AVPlayerItem that does not contain any track.
- iPhone XS, corporate managed device