How to correctly load video selected with PHPickerViewController?

Hello!

I am playing around with the PHPickerViewController and so far I was able to get the selected images by loading them into UIImage instances but I don't know how to get the selected video.

Below is the relevant implementation of the method: func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]):

Code Block
let provider = result.itemProvider
guard provider.hasItemConformingToTypeIdentifier(AVFileType.mov.rawValue) else { return }
provider.loadItem(forTypeIdentifier: AVFileType.mov.rawValue, options: nil) { (fileURL, error) in
if let error = error {
print(error)
return
}
guard let videoURL = fileURL as? URL else { return }
DispatchQueue.main.async {
let fm = FileManager.default
let destination = fm.temporaryDirectory.appendingPathComponent("video123.mov")
try! fm.copyItem(at: videoURL, to: destination)
let playerVC = AVPlayerViewController()
playerVC.player = AVPlayer(url: destination)
self.present(playerVC, animated: true, completion: nil)
}
}


I get crash trying to copy the item. It says the source file does not exists but the path looks real to me.

"The file “3C2BCCBC-4474-491B-90C2-93DF848AADF5.mov” couldn’t be opened because there is no such file."

I tried it without copying first and just passing the URL to AVPlayer but nothing would play.

I am testing this on a simulator.

Thanks for help!



Replies

This seems like a bug on our side. Please file a Feedback with sysdiagnose attached. Thank you!
It looks like your code is trying to copy the file on disk in a block that dispatched async to the main thread. This defers the execution of the block to a later point. What likely happens with the code in your snipped is that the item provider completion handler returned before the file was copied. The system is free to delete to file after the completion handler returns and when the copy happens on the main thread the file is already gone.

Please copy the file to a place you control before the completion handler returns.

Since you are working with a file representation of the item, I'd also recommend to switch to loadFileRepresentation(forTypeIdentifier:completionHandler:) for this case which guarantees a URL as type.
The documentation of that method also states the copy requirement more explicitly:

This method writes a copy of the file’s data to a temporary file, which the system deletes when the completion handler returns.

Thanks! Just tried the loadFileRepresentation method but in the completionHandler I am getting the following error:


Error copying file type com.apple.quicktime-movie. Error: Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type com.apple.quicktime-movie" UserInfo={NSLocalizedDescription=Cannot load representation of type com.apple.quicktime-movie, NSUnderlyingError=0x600002153450 {Error Domain=NSItemProviderErrorDomain Code=-1 "Cannot copy file at URL file:///Users/filip/Library/Developer/CoreSimulator/Devices/7197B12A-3B5C-467E-99DD-B9A5C8DC7211/data/Containers/Shared/AppGroup/44298F88-8E9E-49E8-BCA3-E68877DC0BFC/File%20Provider%20Storage/3C2BCCBC-4474-491B-90C2-93DF848AADF5.mov." UserInfo={NSLocalizedDescription=Cannot copy file at URL file:///Users/filip/Library/Developer/CoreSimulator/Devices/7197B12A-3B5C-467E-99DD-B9A5C8DC7211/data/Containers/Shared/AppGroup/44298F88-8E9E-49E8-BCA3-E68877DC0BFC/File%20Provider%20Storage/3C2BCCBC-4474-491B-90C2-93DF848AADF5.mov., NSUnderlyingError=0x6000021534e0 {Error Domain=NSItemProviderErrorDomain Code=-1 "Cannot create a temporary file. Error: Undefined error: 0" UserInfo={NSLocalizedDescription=Cannot create a temporary file. Error: Undefined error: 0}}}}}

Looks like it is indeed a bug?




Ok, this looks like an issue we are already tracking (64630315).
Any update on this?
64630315 has been resolved. Note that you need to remove fm.copyItem() outside your DispatchQueue.main.async to make sure your source file isn't removed.
Update on the iOS 14 Beta 6:

On my side, this:
Code Block swift
provider.loadItem(forTypeIdentifier: "public.movie", options: nil) { /* do stuff */ }

Gives me a URL pointing to a file that does not exist. So until now, it seems impossible to load a video from the new PHPickerViewController.

Is this a known issue?

Can confirm @martinzenly 's observations. I'm on iOS 14 beta 6, Xcode beta 6.

Getting non-existent URLs for videos with itemProvider.loadItem (sometimes, not always). Both local and from iCloud.

Switching to itemProvider.loadFileRepresentation, however, seems to work. Although that method itself brings with it two new issues:
  1. It is incredibly slow to return local videos (in the range of several seconds, unacceptable).

  2. There doesn't appear to be a way to track iCloud download progress (required for larger videos).

Among a ton of other issues. At least for me, the picker remains unusable for now.
Yes, please use itemProvider.loadFileRepresentation to handle large files (e.g. videos).

It is incredibly slow to return local videos (in the range of several seconds, unacceptable).

It's likely that the picker is transcoding the video for you on the fly. You can set preferredAssetRepresentationMode to .current to avoid transcoding if your app can handle HEVC videos.

There doesn't appear to be a way to track iCloud download progress (required for larger videos).

It's currently not supported. Please file a Feedback request if possible.
Thank you for your helpful clarifications!
  1. About slowness: preferredAssetRepresentationMode was indeed the missing link. I had not seen this API before.

  2. iCloud Tracking: I have submitted an enhancement request (FB8585202)

For reference, for people having trouble getting video picking to work like I did, here are the steps:
  1. Optional: Set the picker's configuration's preferredAssetRepresentationMode to current.

  2. Use itemProvider.loadFileRepresentation instead of itemProvider.loadItem.

  3. In the completion handler, copy or move the file at the provided URL to a location you control. This must complete before the completion handler returns.

  4. Now you can use the video.

Thanks again!

In testing on Xcode 12.0 beta 6, in the iOS 14 Simulator, when attempting to import a QuickTime movie from PHPicker using itemProvider.loadFileRepresentation, I got the following error: "Cannot load representation of type com.apple.quicktime-movie". I was able to workaround this by using PHPickerConfigurationAssetRepresentationModeCurrent.

Also in testing, I found that itemProvider.loadFileRepresentation does not work for live photos. I had to use itemProvider.loadInPlaceFileRepresentation instead.


Hope this helps!


  1. It only affects simulator (63426347).

  2. Please use loadObjectOfClass to load PHLivePhoto objects. Live Photos' file representations are not supported.

Is this problem solved?

Still encounter this problem after 3 years.

Device: iPhone 15 iOS 17

If the video is in iCloud, the problem may be shown. (not every video has problem)

The error code is -1000