Home
Playing Videos from Photo Library on iOS
Intro
I mostly write iOS code these days, and was exploring some API's to learn and play around. Though generally iOS API's are very clean and easy to use, this one had me spend a few hours to figure out and I realized that there is not a good comprehensive guide on how to do it. That is selecting a local video from the photo library and displaying it in the app.
Seems pretty simple, but has some nuanced points.
Selecting Video Asset
First, we need to fetch the asset from the photos library. iOS already gives us an interface to complete this operation via PHPickerViewController
. Simply show this view controller following a user action.
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.filter PHPickerFilter.any(of: [ .videos]) // Only include videos.
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self // Make sure to assign a delegate.
present(picker, animated: true)
Note that assigning delegate requires the entity to conform to PHPickerViewControllerDelegate
. Conforming to this protocol is the second part of importing the video.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// If no assets are selected, dismiss the view controller.
if (results.count < 1) {
picker.dismiss(animated: true)
return
}
let selectedAsset = results[0]
loadVideoAsset(asset: selectedAsset)
picker.dismiss(animated: true)
}
Seems pretty straightforward, right. Well, wait until we deep dive into loadVideoAsset
method. ๐
Loading Video Asset
PHPickerViewController
gives us a PHPickerResult
, which is a type that represents the library asset. Since videos are large files, the API uses this meta type to make working with large files easier.
Next up, we're going to use PHPickerResult.itemProvider
to load the contents of this video into our view.
func loadVideoAsset(asset: PHPickerResult) {
let itemProvider = asset.itemProvider
itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { url, error in
guard let videoURL = url else { return }
// Copy video to a temporary location.
let videoExtension = videoURL.pathExtension
let fileManager = FileManager.default
let destination = fileManager.temporaryDirectory.appendingPathComponent("imported.\(videoExtension)")
let fileExists = fileManager.fileExists(atPath: destination.path())
if (fileExists){
try! fileManager.removeItem(at: destination)
}
try! fileManager.copyItem(at: videoURL, to: destination)
// Play the video.
DispatchQueue.main.async {
self.playVideo(url: destination)
}
}
}
The most confusing part of this entire tutorial lies in this method if you ask me. And to realize it, one must read the following part of the documentation for loadFileRepresentation(forTypeIdentifier:completionHandler:)
very carefully.
This method writes a copy of the fileโs data to a temporary file, which the system deletes when the completion handler returns.
So this is why the video should be stored in a temporary location to be used in the app before the completion handler returns. And this is where the file manager comes in handy. See how in lines 6-13 we are copying the file to a temporary location.
Playing the video
Last step is to use the AVPlayer
to play the video in a view.
func playVideo(url: URL){
let playerItem = AVPlayerItem(url: url)
let player = AVPlayer(playerItem: playerItem)
videoViewController.player = player
player.play()
}
This step assumes you've set up
AVPlayerViewController
on a view, or you can also use it display the video on a full screen experience.
And ta da, your video should be playing, hopefully! ๐