Upload a video to firebase using Swift - ios

I am trying to pick a video from the camera roll and then uploading that video to firebase storage. So far I am able to pick a video but it is not uploading to firebase, how can I upload it to firebase storage?
func uploadVideoToDB(url: URL){
let storageReference = Storage.storage().reference().child("video.mov")
storageReference.putFile(from: url)
}
func fetchVideos(section: Int){
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
imagePickerController.mediaTypes = ["public.movie"]
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){
let url = info[UIImagePickerController.InfoKey(rawValue: "UIImagePickerControllerReferenceURL")] as? NSURL
DataService.instance.uploadVideoToDB(url: url! as URL)
imagePickerController.dismiss(animated: true, completion: nil)
}

Call this function to upload the video to firebase storage
func uploadTOFireBaseVideo(url: URL,
success : #escaping (String) -> Void,
failure : #escaping (Error) -> Void) {
let name = "\(Int(Date().timeIntervalSince1970)).mp4"
let path = NSTemporaryDirectory() + name
let dispatchgroup = DispatchGroup()
dispatchgroup.enter()
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let outputurl = documentsURL.appendingPathComponent(name)
var ur = outputurl
self.convertVideo(toMPEG4FormatForVideo: url as URL, outputURL: outputurl) { (session) in
ur = session.outputURL!
dispatchgroup.leave()
}
dispatchgroup.wait()
let data = NSData(contentsOf: ur as URL)
do {
try data?.write(to: URL(fileURLWithPath: path), options: .atomic)
} catch {
print(error)
}
let storageRef = Storage.storage().reference().child("Videos").child(name)
if let uploadData = data as Data? {
storageRef.putData(uploadData, metadata: nil
, completion: { (metadata, error) in
if let error = error {
failure(error)
}else{
let strPic:String = (metadata?.downloadURL()?.absoluteString)!
success(strPic)
}
})
}
}
Following function converts the video to mp4 format so that it can be viewed on any device either it be iOS or android
func convertVideo(toMPEG4FormatForVideo inputURL: URL, outputURL: URL, handler: #escaping (AVAssetExportSession) -> Void) {
try! FileManager.default.removeItem(at: outputURL as URL)
let asset = AVURLAsset(url: inputURL as URL, options: nil)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)!
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
exportSession.exportAsynchronously(completionHandler: {
handler(exportSession)
})
}

This is what did it for me:
needed to convert url into Data then use putData instead of putFile
func uploadVideoToDB(url: URL){
let filename = UUID().uuidString
let ref = Storage.storage().reference().child("videos").child("\(filename).mp4")
do {
let videoData = try Data(contentsOf: url)
ref.putData(videoData)
} catch {
print(error)
}
}

Related

How to get URL in Photo Library?

I am picking a video and I want to upload it to Firebase but there is an error in my code. I think the error is because of the URL. I tried lots of methods to get the URL but nothing worked.
This is my code below:
#IBAction func showImagePicker() {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = false
picker.sourceType = .savedPhotosAlbum
picker.mediaTypes = [kUTTypeMovie as String]
picker.videoQuality = .typeIFrame1280x720
self.present(picker, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil);
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
let videoName = UUID().uuidString
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(videoName)
print("videoName: \(videoName)")
print("url: \(url)")
let metadata = StorageMetadata() //get metadata of firebase
metadata.contentType = "video/quicktime"
let storageRef = storage.reference() //get storage reference of the firebase storage
let videoRef = storageRef.child("videos/\(videoName)") //Get a folder where the video will be saved
videoRef.putFile(from: url, metadata: nil) { (metadata, error) in //uploading to the firebase storage folder
guard let metadata = metadata else {
//If there is error in meta data, print "error"
print("error\(error)")
return
}
print("Put us complete and I got this back: \(String(describing: metadata))")
// Metadata contains file metadata such as size, content-type.
let size = metadata.size
// Access to download URL after upload.
videoRef.downloadURL { (url, error) in
guard let downloadURL = url else {
print("Got an generating the URL:)")
// Uh-oh, an error occurred!
return
}
}
}
print("Done")
}
This is the error below:
Optional(Error Domain=FIRStorageErrorDomain Code=-13000 "File at URL: file:///var/mobile/Containers/Data/Application/3BE603CC-A936-43CF-8127-EB224EC0D7ED/Documents/96267AEB-F61D-4985-8C6D-FD641DEAD72D is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url." UserInfo={NSLocalizedDescription=File at URL: file:///var/mobile/Containers/Data/Application/3BE603CC-A936-43CF-8127-EB224EC0D7ED/Documents/96267AEB-F61D-4985-8C6D-FD641DEAD72D is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url.})
I want to upload the selected video to Firebase storage.
I had done uploading videos to Firebase Storage recently. Try this:
For Video Picker:
import Foundation
import SwiftUI
import PhotosUI
struct VideoPicker: UIViewControllerRepresentable {
var onVideoPicked : (URL, String) -> Void
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.filter = .videos
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self, onVideoPicked: self.onVideoPicked)
}
class Coordinator:NSObject, PHPickerViewControllerDelegate{
let parent:VideoPicker
var onVideoPicked : (URL, String) -> Void
init(_ parent: VideoPicker, onVideoPicked: #escaping (URL, String) -> Void){
self.parent = parent
self.onVideoPicked = onVideoPicked
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true) {
// do something on dismiss
}
guard let provider = results.first?.itemProvider else {return}
provider.loadFileRepresentation(forTypeIdentifier: "public.movie") { url, error in
guard error == nil else{
print(error?.localizedDescription ?? "")
return
}
// receiving the video-local-URL / filepath
guard let url = url else {return}
// create a new filename
let fileName = "\(Int(Date().timeIntervalSince1970)).\(url.pathExtension)"
// create new URL
let newUrl = URL(fileURLWithPath: NSTemporaryDirectory() + fileName)
// copy item to APP Storage
try? FileManager.default.copyItem(at: url, to: newUrl)
self.onVideoPicked(newUrl, fileName)
}
}
}
}
This will return your video URL and the video name
Use this in SwiftUIView as:
.sheet(isPresented: $vm.openVideos, content: {
VideoPicker(onVideoPicked: { url, videoName in
let videoRef = storageRef.child("videos/\(videoName)")
videoRef.putFile(from: url, metadata: nil) { metadata, error in
if error != nil {
Helper.shared.printDebug("\(String(describing: error)) and \(error?.localizedDescription ?? "error")")
return
}
videoRef.downloadURL(completion: { url, error in
guard let url = url else {
print("Failed to get download url")
return
}
print(url.absoluteString)
})
}
})
})

Export files from Music Library to Documents directory (Swift 5)

In my app I want to export files from Music Library and save them to Documents directory. I already fetch file list from Music Library and I get url of each file. But I still have two problems:
I can export files only in "m4a" format, but I'd like to export it in format what file was in Music Library (mp3, wav, aac or else)
How can I save files from Music Library to Documents directory with url?
My code now looks like this:
private func exportFile(with assetURL: URL, completionHandler: #escaping (_ fileURL: URL?, _ error: Error?) -> ()) {
let asset = AVURLAsset(url: assetURL)
guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
completionHandler(nil, nil)
return
}
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(NSUUID().uuidString)
.appendingPathExtension("m4a")
exporter.outputURL = fileURL
exporter.outputFileType = AVFileType.m4a
exporter.exportAsynchronously {
if exporter.status == .completed {
completionHandler(fileURL, nil)
} else {
completionHandler(nil, exporter.error)
}
}
func downloadFile(file: MPMediaItem?, callback: #escaping () -> ()) {
guard let file = file,
let url = file.url
else { return }
exportFile(with: url) { fileURL, error in
guard let fileURL = fileURL, error == nil else {
print(error?.localizedDescription)
return
}
//Here I'd like to save file to Documents dir
print("\(fileURL)")
}
callback()
}
Solution for second question.
I put the code in one method and save directly to the Documents folder, not to a temporary folder:
func downloadFile(file: MPMediaItem?, callback: #escaping () -> ()) {
guard let file = file,
let url = file.assetURL
else { return }
let exportSession = AVAssetExportSession(asset: AVAsset(url: url), presetName: AVAssetExportPresetAppleM4A)
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.outputFileType = AVFileType.m4a
let documentURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let outputURL = documentURL.appendingPathComponent("\(file.title!).m4a")
// //Delete Existing file if needs
// do {
// try FileManager.default.removeItem(at: outputURL)
// } catch let error as NSError {
// print(error.debugDescription)
// }
exportSession?.outputURL = outputURL
exportSession?.exportAsynchronously(completionHandler: { () -> () in
if exportSession!.status == AVAssetExportSession.Status.completed {
print("Export Successful")
DispatchQueue.main.async {
//Update IU
callback()
}
} else {
print("Export failed")
}
})
}

How to upload to S3 bucket taking a photo with the iOS Camera

I am trying to use the AWS S3 bucket to store user photos from when they have taken them from their phones. I right now have my code set up to the point where the user is able to take a photo of something and have that show up on the UIImageView.
The issue I am encountering is that I have no clue how to store it on the S3 bucket, I have code right now that is able to store a specified photo the bucket, but not really code that is able to store a photo that is taken from the camera.
Take Photo code
#IBAction func takePhoto(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerController.SourceType.camera
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
takenPhoto.contentMode = .scaleToFill
takenPhoto.image = pickedImage
print(takenPhoto.image = pickedImage)
}
picker.dismiss(animated: true, completion: nil)
}
AWS S3 Bucket Code
#IBAction func uploadFile(_ sender: Any) {
uploadFile(with: "eartj", type: ".jpeg")
}
func uploadFile(with resource: String, type: String){
let key = "\(resource),\(type)"
let imagePath = Bundle.main.path(forResource: resource, ofType: type)!
let imageUrl = URL(fileURLWithPath: imagePath)
let request = AWSS3TransferManagerUploadRequest()!
request.bucket = "wuuurktest"
request.key = key
request.body = imageUrl
request.acl = .publicReadWrite
let transferManager = AWSS3TransferManager.default()
transferManager.upload(request).continueWith(executor: AWSExecutor.mainThread()) { (task) -> Any? in
if let error = task.error {
print(error)
}
if task.result != nil {
print("Uploaded File")
}
return nil
}
}
Link to the guide I am using to create the file upload
https://www.youtube.com/watch?v=UMgApUhg7ic
Most of the answers are outdated and too complicated. I was struggling with the same problem and finally found a solution.
This works best for me and works on Swift 5.
First of all, let's update the function to upload images to AWS.
func uploadToS3(url: URL) {
let fileArr = url.path.components(separatedBy: "/") // Path will be too long, so you have to separate the elements by / and store in an array
let key = fileArr.last // We get the last element of the array which in our case will be the image (my-image.jpg)
let localImageUrl = url
let request = AWSS3TransferManagerUploadRequest()!
request.bucket = bucketName
request.key = key
request.body = localImageUrl
request.acl = .publicReadWrite
let transferManager = AWSS3TransferManager.default()
transferManager.upload(request).continueWith(executor: AWSExecutor.mainThread()) { (task) -> Any? in
if let error = task.error {
print(error)
}
if task.result != nil {
print("Uploaded \(key)")
let contentUrl = self.s3Url.appendingPathComponent(bucketName).appendingPathComponent(key!)
self.contentUrl = contentUrl
}
return nil
}
}
In this block of code:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
takenPhoto.contentMode = .scaleToFill
takenPhoto.image = pickedImage
print(takenPhoto.image = pickedImage)
// Add here:
let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent("my-image", isDirectory: false)
.appendingPathExtension("jpg") /* here we are naming the image 'my-image' and it will be 'jpg', if you want you can add a counter to increase the number each time you upload an image, and you make something like this: "my-image-\(counter)"*/
// Then write to disk
if let data = pickedImage.jpegData(compressionQuality: 0.8) {
do {
try data.write(to: url)
uploadToS3(url: url) //Call the updated function to store to AWS bucket
} catch {
print("Handle the error, i.e. disk can be full")
}
}
}
picker.dismiss(animated: true, completion: nil)
}
With this implementation, the image will be uploaded immediately to the server once you select the image from the library.
First thing you need to do is to store the picked image in your app's document directory as a temporary file. As soon as your image is picked, save it to the document directory using the below function.
func saveFileToDocumentDirectory(file: Data, fileExtension: String, folderName: String) -> URL? {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd_HHmmss"
let stringOfDateTimeStamp = formatter.string(from: Date())
print("Date time stamp String: \(stringOfDateTimeStamp)")
let directoryPath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("\(folderName)/")
if !FileManager.default.fileExists(atPath: directoryPath) {
do {
try FileManager.default.createDirectory(at: NSURL.fileURL(withPath: directoryPath), withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
}
}
let filename = "/\(stringOfDateTimeStamp)_\(fileExtension)"
let customPath = "\(folderName)\(filename)"
let filepath = directoryPath+filename
print("FilePATH: \(filepath)")
let url = NSURL.fileURL(withPath: filepath)
do {
try file.write(to: url, options: .atomic)
print("CustomPAth:\(customPath)")
print(String.init("\(directoryPath)\(filename)"))
return url
} catch {
print(error)
print("file cant not be save at path \(filepath), with error : \(error)");
return nil
}
}
This will return a URL and you can then use the below function to upload that file to your S3 bucket.
func uploadToS3(url: URL, contentType: String, fileExtension: String){
SwiftLoader.show(title: "Uploading File", animated: true)
let accessKey = "YOUR_ACCESS_KEY"
let secretKey = "YOUR_SECRET_KEY"
let credentialsProvider = AWSStaticCredentialsProvider(accessKey: accessKey, secretKey: secretKey)
let configuration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let remoteName = "IMG_\(UUID().uuidString)"+".\(fileExtension)"
let S3BucketName = "YOUR_BUCKET_NAME"
let uploadRequest = AWSS3TransferManagerUploadRequest()!
uploadRequest.body = url
uploadRequest.key = remoteName
uploadRequest.bucket = S3BucketName
uploadRequest.contentType = contentType
uploadRequest.acl = .publicRead
let transferManager = AWSS3TransferManager.default()
transferManager.upload(uploadRequest).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Upload failed with error: (\(error.localizedDescription))")
DispatchQueue.main.async {
print("An error occurred while Uploading your file, try again.")
SwiftLoader.hide()
}
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(uploadRequest.bucket!).appendingPathComponent(uploadRequest.key!)
print("Uploaded to:\(String(describing: publicURL))")
}
return nil
})
}
Don't forget to delete your temporary file once your upload is successful.
Here is an example using TransferUtility:-
import AWSCognitoIdentityProvider
import AWSS3
typealias progressBlock = (_ progress: Double) -> Void
typealias completionBlock = (_ response: Any?, _ error: Error?) -> Void
//using Utility upload expression
func uploadImage(with image: URL, key: String?, progress: progressBlock?, completion: completionBlock?) {
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { (task: AWSS3TransferUtilityTask, awsProgress: Progress) -> Void in
//print(awsProgress.fractionCompleted)
guard let uploadProgress = progress else { return }
DispatchQueue.main.async {
uploadProgress(awsProgress.fractionCompleted)
}
}
expression.setValue("public-read-write", forRequestHeader: "x-amz-acl")
expression.setValue("public-read-write", forRequestParameter: "x-amz-acl")
// Completion block
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if error == nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(AWS.bucketName).appendingPathComponent(key!)
print("Uploaded to:\(String(describing: publicURL))")
if let completionBlock = completion {
completionBlock(publicURL?.absoluteString, nil)
}
} else {
if let completionBlock = completion {
completionBlock(nil, error)
}
}
})
}
// Start uploading using AWSS3TransferUtility
let awsTransferUtility = AWSS3TransferUtility.default()
awsTransferUtility.uploadFile(
image as URL,
bucket: AWS.bucketName, //Make sure you write the correct bucket name here
key: key!, //"private/{user_identity_id}/my-picture.png"
contentType: "image/png",
expression: expression,
completionHandler: completionHandler).continueWith(block: { (task) -> Any? in
if let error = task.error {
print("error is: \(error.localizedDescription)")
}
if let _ = task.result {
// your uploadTask
print("Starting upload...")
}
return nil
})
}
Two parameters I am passing:-
image: URL and key: String?
Here is how I get image and image name (key):-
//setting temp name for upload // I am using a random string here
let imageName = "\(CommonMethod.randomString(length: 6))" + ".png"
//settings temp location for image
let tempDirectoryUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(imageName)
guard let localUrlPath = image!.save(at: tempDirectoryUrl) else { return }
//URL
print(localUrlPath)
Happy coding!

How to find media file type using MPMediaItemPropertyMediaType like mp3/mpeg/m4a in Swift iOS

I am developing a music app in Swift3. Where I need to select any audio song from device gallery and upload this to Amazon S3. Here is my code :
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection)
{
self.dismiss(animated: true, completion: nil)
let item: MPMediaItem? = (mediaItemCollection.items[0] as? MPMediaItem)
let url: URL? = item?.value(forProperty: MPMediaItemPropertyAssetURL) as! URL?
let albumTitle = item?.value( forProperty: MPMediaItemPropertyTitle ) as! String
let albumMediaType = item?.value( forProperty: MPMediaItemPropertyMediaType) as! Int
}
As an output, I am getting Asset Url : ipod-library://item/item.mp3?id=8739819345820495362 And Media Type is coming in integer 1/2/3/4....
Now, my problem is I need to know the file type of the selected song, because when I am adding hard code '.mp3' to file and upload this to Amazon S3 then some of audio files are playing and rest is not!
Can you please help me out to resolve this issue? Thank you.
Edit: Here is my code to convert AssetUrl to path :
func exportMedia(_ assetURL: URL, completionHandler: #escaping (_ fileURL: URL?, _ error: Error?) -> ()) {
let asset = AVURLAsset(url: assetURL)
guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
completionHandler(nil, ExportError.unableToCreateExporter)
return
}
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(NSUUID().uuidString)
.appendingPathExtension("m4a")
exporter.outputURL = fileURL
exporter.outputFileType = "com.apple.m4a-audio"
exporter.exportAsynchronously {
if exporter.status == .completed {
completionHandler(fileURL, nil)
} else {
completionHandler(nil, exporter.error)
}
}
}

How to Get Video from a Live Photo in iOS

I'm trying to figure it out, but can't find any useful information.
I only found this:
PHAssetResourceManager.defaultManager().writeDataForAssetResource(assetRes,
toFile: fileURL, options: nil, completionHandler:
{
// Video file has been written to path specified via fileURL
}
but I'm ashamed to say I have no idea how to play it out.
I've created a UIImagePickerController and loaded an Image from the Camera Roll.
Use this code to get the video from live photo:
- (void)videoUrlForLivePhotoAsset:(PHAsset*)asset withCompletionBlock:(void (^)(NSURL* url))completionBlock{
if([asset isKindOfClass:[PHAsset class]]){
NSString* identifier = [(PHAsset*)asset localIdentifier];
NSString* filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mov",[NSString stringWithFormat:#"%.0f",[[NSDate date] timeIntervalSince1970]]]];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
PHLivePhotoRequestOptions* options = [PHLivePhotoRequestOptions new];
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
options.networkAccessAllowed = YES;
[[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:[UIScreen mainScreen].bounds.size contentMode:PHImageContentModeDefault options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
if(livePhoto){
NSArray* assetResources = [PHAssetResource assetResourcesForLivePhoto:livePhoto];
PHAssetResource* videoResource = nil;
for(PHAssetResource* resource in assetResources){
if (resource.type == PHAssetResourceTypePairedVideo) {
videoResource = resource;
break;
}
}
if(videoResource){
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:fileUrl options:nil completionHandler:^(NSError * _Nullable error) {
if(!error){
completionBlock(fileUrl);
}else{
completionBlock(nil);
}
}];
}else{
completionBlock(nil);
}
}else{
completionBlock(nil);
}
}];
}else{
completionBlock(nil);
}
}
Basically what you have to do is that you first need to fetch the PHLivePhoto object from your PHAsset. After that, you will have to traverse all the asset resources within your live photo and check if it is of type PHAssetResourceTypePairedVideo.
If yes, you got your video. Now you will require to save it to some temporary directory as I did here and use this file for whatever purpose you may have.
To Play this video, you can use the following code:
NSURL *videoURL = [NSURL fileURLWithPath:fileUrl];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
AVPlayerViewController *playerViewController = [AVPlayerViewController new];
playerViewController.player = player;
[self presentViewController:playerViewController animated:YES completion:nil];
Feel free to ask if you need any clarification.
P.S.- I made a few changes in this method to remove dependency of my application's code so the above code is untested, however I feel it should work as expected.
Swift 4 version
import Photos
import MobileCoreServices
// <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
#IBAction func showImagePicker(sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self;
picker.allowsEditing = false;
picker.sourceType = .photoLibrary;
picker.mediaTypes = [kUTTypeLivePhoto as String, kUTTypeImage as String];
present(picker, animated: true, completion: nil);
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard
let livePhoto = info[UIImagePickerControllerLivePhoto] as? PHLivePhoto,
let photoDir = generateFolderForLivePhotoResources()
else {
return;
}
let assetResources = PHAssetResource.assetResources(for: livePhoto)
for resource in assetResources {
// SAVE FROM BUFFER
// let buffer = NSMutableData()
// PHAssetResourceManager.default().requestData(for: resource, options: nil, dataReceivedHandler: { (chunk) in
// buffer.append(chunk)
// }, completionHandler: {[weak self] error in
// self?.saveAssetResource(resource: resource, inDirectory: photoDir, buffer: buffer, maybeError: error)
// })
// SAVE DIRECTLY
saveAssetResource(resource: resource, inDirectory: photoDir, buffer: nil, maybeError: nil)
}
picker.dismiss(animated: true) {}
}
func saveAssetResource(
resource: PHAssetResource,
inDirectory: NSURL,
buffer: NSMutableData?, maybeError: Error?
) -> Void {
guard maybeError == nil else {
print("Could not request data for resource: \(resource), error: \(String(describing: maybeError))")
return
}
let maybeExt = UTTypeCopyPreferredTagWithClass(
resource.uniformTypeIdentifier as CFString,
kUTTagClassFilenameExtension
)?.takeRetainedValue()
guard let ext = maybeExt else {
return
}
guard var fileUrl = inDirectory.appendingPathComponent(NSUUID().uuidString) else {
print("file url error")
return
}
fileUrl = fileUrl.appendingPathExtension(ext as String)
if let buffer = buffer, buffer.write(to: fileUrl, atomically: true) {
print("Saved resource form buffer \(resource) to filepath \(String(describing: fileUrl))")
} else {
PHAssetResourceManager.default().writeData(for: resource, toFile: fileUrl, options: nil) { (error) in
print("Saved resource directly \(resource) to filepath \(String(describing: fileUrl))")
}
}
}
func generateFolderForLivePhotoResources() -> NSURL? {
let photoDir = NSURL(
// NB: Files in NSTemporaryDirectory() are automatically cleaned up by the OS
fileURLWithPath: NSTemporaryDirectory(),
isDirectory: true
).appendingPathComponent(NSUUID().uuidString)
let fileManager = FileManager()
// we need to specify type as ()? as otherwise the compiler generates a warning
let success : ()? = try? fileManager.createDirectory(
at: photoDir!,
withIntermediateDirectories: true,
attributes: nil
)
return success != nil ? photoDir! as NSURL : nil
}
In depth tutorial here Live Photo API on iOS
The question is a little confusing
First, If you want to pick live photo and play live photo.I recommend you use the Photos Framework instead of UIImagePickerController. This way you can fetch the asset and have more control. Then you can play the live photo as mov or the muted version with PHLivePhotoView by setting the startPlayback(with:) to hint or full.
You can refer the code here :
a github repo LivePreview show you how to select live photo and play it.
Second, if you want convert live photo to mov, the code you pasted will work, and if you want to play mov directly, you may want use AVPlayer
Plus, WWDC provides Example app using Photos framework
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let phAsset = info[.phAsset] as? PHAsset
imagePickerController.dismiss(animated: true, completion: nil)
let style = phAsset?.playbackStyle
if(style != .livePhoto) {
print("This is not a live photo")
return
}
let filePath = NSTemporaryDirectory() + String(format: "%.0f", NSDate().timeIntervalSince1970) + "_.mov"
let fileURL = NSURL(fileURLWithPath: filePath)
let options = PHLivePhotoRequestOptions()
options.deliveryMode = .fastFormat
options.isNetworkAccessAllowed = true
PHImageManager.default().requestLivePhoto(for: phAsset!, targetSize: CGSize(width: 1920, height: 1080), contentMode: PHImageContentMode.default, options: options) { livePhoto, info in
if((livePhoto) != nil) {
let assetResources = PHAssetResource.assetResources(for: livePhoto!)
var videoResource : PHAssetResource?
for resources in assetResources {
if(resources.type == .pairedVideo) {
videoResource = resources
break
}
}
guard let videoResource = videoResource else {
fatalError("video resource is nil")
}
PHAssetResourceManager.default().writeData(for: videoResource, toFile: fileURL as URL, options: nil) { error in
let avAsset : AVAsset = AVAsset(url: fileURL as URL)
DispatchQueue.main.async { [self] in
// Whatever you do using fileURL or avAsset.
}
}
}
}
}
Swift 5
func videoUrlForLivePhotoAsset(asset: PHAsset, completionHandler: #escaping (_ result: URL?) -> Void) {
print("videoUrlForLivePhotoAsset: \(asset)")
let options : PHLivePhotoRequestOptions = PHLivePhotoRequestOptions.init()
options.deliveryMode = .fastFormat
options.isNetworkAccessAllowed = true
PHImageManager.default().requestLivePhoto(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: .default, options: options) { (livePhoto, info) in
if livePhoto != nil {
let assetResources : [PHAssetResource] = PHAssetResource.assetResources(for: livePhoto!)
var videoResource : PHAssetResource?
for resource in assetResources {
if resource.type == .pairedVideo {
videoResource = resource
break
}
}
guard let photoDir = self.generateFolderForLivePhotoResources() else {
return
}
print("videoResource: \(videoResource)")
if videoResource != nil {
self.saveAssetResource(resource: videoResource!, inDirectory: photoDir, buffer: nil, maybeError: nil) { (fileUrl) in
completionHandler(fileUrl)
}
}
} else {
completionHandler(nil)
}
}
}
func saveAssetResource(
resource: PHAssetResource,
inDirectory: NSURL,
buffer: NSMutableData?, maybeError: Error?, completionHandler: #escaping (_ result: URL?) -> Void) {
guard maybeError == nil else {
print("Could not request data for resource: \(resource), error: \(String(describing: maybeError))")
return
}
let maybeExt = UTTypeCopyPreferredTagWithClass(
resource.uniformTypeIdentifier as CFString,
kUTTagClassFilenameExtension
)?.takeRetainedValue()
guard let ext = maybeExt else {
return
}
guard var fileUrl = inDirectory.appendingPathComponent(NSUUID().uuidString) else {
print("file url error")
return
}
fileUrl = fileUrl.appendingPathExtension(ext as String)
if let buffer = buffer, buffer.write(to: fileUrl, atomically: true) {
print("Saved resource form buffer \(resource) to filepath \(String(describing: fileUrl))")
completionHandler(fileUrl)
} else {
PHAssetResourceManager.default().writeData(for: resource, toFile: fileUrl, options: nil) { (error) in
print("Saved resource directly \(resource) to filepath \(String(describing: fileUrl))")
if error == nil {
completionHandler(fileUrl)
} else {
completionHandler(nil)
}
}
}
}
func generateFolderForLivePhotoResources() -> NSURL? {
let photoDir = NSURL(
// NB: Files in NSTemporaryDirectory() are automatically cleaned up by the OS
fileURLWithPath: NSTemporaryDirectory(),
isDirectory: true
).appendingPathComponent(NSUUID().uuidString)
let fileManager = FileManager()
// we need to specify type as ()? as otherwise the compiler generates a warning
let success : ()? = try? fileManager.createDirectory(
at: photoDir!,
withIntermediateDirectories: true,
attributes: nil
)
return success != nil ? photoDir! as NSURL : nil
}
Invoke with the following:
let asset = PHAsset.init()
self.videoUrlForLivePhotoAsset(asset: asset!) { (url) in
print("url: \(url)")
}
Note: You need to clean up the Temp and Documents directory, and remove the files.

Resources