Swift: save video from NSURL to user camera roll
Asked Answered
T

7

38

I have a variable videoURL of type NSURL.

If I call println(videoURL) it would return something like this: http://files.parsetfss.com/d540f71f-video.mp4

I have a button set up that should take this videoURL and save the video to the user's camera roll.

The best I have done is this:

UISaveVideoAtPathToSavedPhotosAlbum(videoPath: String!, completionTarget: AnyObject!, completionSelector: Selector, contextInfo: UnsafeMutablePointer<Void>)

While I'm not even sure if this will work or not, I can't figure out how to convert videoFile:NSURL into a videoPath.

Any help is appreciated on this.

Edit:

The following is unsuccessful:

UISaveVideoAtPathToSavedPhotosAlbum(videoURL.relativePath, self, nil, nil)
Thill answered 7/4, 2015 at 2:31 Comment(4)
That is a remote URL. You cannot save it, because you do not have it. You are going to have to download that file first. When it has downloaded, use the download URL (the URL of the file on disk) and save that to the camera roll. All of that is going to be extremely time-consuming, so you will have to do in a background thread.Messer
Although I've only been trying for 30 mins, turning the remote NSURL into a local file is proving quite difficult. Let me know if you know the best way to do this or have any links. I'm trying NSURLConnection and it's confusing.Thill
Nowadays NSURLSession is easier. And using a download task is trivial. Here's an example: github.com/mattneub/Programming-iOS-Book-Examples/blob/master/… In that example, I download an image and then put it into the interface. You will download a video and then save it into the camera roll. It's basically the same idea!Messer
UISaveVideoAtPathToSavedPhotosAlbum works just fine in XCode 15 iOS 17, in my custom camera implementation.Chlortetracycline
D
69

AssetsLibrary is deprecated

1: import Photos

import Photos

2: Use this code to save video from url to camera library.

PHPhotoLibrary.sharedPhotoLibrary().performChanges({
             PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(nsUrlToYourVideo)
         }) { saved, error in
             if saved {
                 let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .Alert) 
                 let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
                 alertController.addAction(defaultAction)
                 self.presentViewController(alertController, animated: true, completion: nil)
             }
         }

Swift 3 & Swift 4

PHPhotoLibrary.shared().performChanges({
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: urlToYourVideo)
}) { saved, error in
    if saved {
        let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
        let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(defaultAction)
        self.present(alertController, animated: true, completion: nil)
    }
}
Denticle answered 3/7, 2016 at 22:58 Comment(4)
Hello, do you know if it possible to get the Url of the video in the Camera Roll ? I try to share it with FBSDKShareVideoContent, and I just can't figure the URL in the camera roll (as FBSDKShareVideoContent requires a video stored in the camera roll).Edessa
Wow, that Swift 3 solution looks familiar!Teleost
Yeah, whoever added the Swift 3 used yours or you both took it from the same place =)Denticle
Is this working only for local urls? Because it doesn't save a remote UrlFlapper
T
16

The accepted answer no longer works with Swift 3.0 & iOS 10.

First, you need to set the following permission in your app's plist file:

Privacy - Photo Library Usage Description

Provide a string that is presented to the user explaining why you are requesting the permission.

Next, import photos:

import Photos

Finally, here is the updated code for Swift 3.0:

PHPhotoLibrary.shared().performChanges({
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileURL)
}) { saved, error in
    if saved {
        let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
        let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(defaultAction)
        self.present(alertController, animated: true, completion: nil)
    }
}
Teleost answered 7/3, 2017 at 1:34 Comment(1)
CodeBender, will this work for remote url as well? Or I need to download it first and than use the local url?Flapper
A
10

To save video from NSURL to user camera roll

func video(videoPath: NSString, didFinishSavingWithError error: NSError?, contextInfo info: AnyObject) 
 {
    if let _ = error {
       print("Error,Video failed to save")
    }else{
       print("Successfully,Video was saved")
    }
}







func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    if let conversationField = self.conversation {

      if (mediaType?.isEqual((kUTTypeMovie as NSString) as String))!
        {
            let theVideoURL: URL? = (info[UIImagePickerControllerMediaURL] as? URL)

            if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum((theVideoURL?.path)!))
            {
                UISaveVideoAtPathToSavedPhotosAlbum((theVideoURL?.path)!, self, #selector(ConversationDetailsViewController.video(videoPath:didFinishSavingWithError:contextInfo:)), nil)
            }   
   }
   self.dismiss(animated: true, completion: nil)
}

Reference from:: https://www.raywenderlich.com/94404/play-record-merge-videos-ios-swift

Anacrusis answered 11/4, 2017 at 11:24 Comment(0)
B
10

Try this instead for saving video in photo library in swift 4.2 and above

func requestAuthorization(completion: @escaping ()->Void) {
        if PHPhotoLibrary.authorizationStatus() == .notDetermined {
            PHPhotoLibrary.requestAuthorization { (status) in
                DispatchQueue.main.async {
                    completion()
                }
            }
        } else if PHPhotoLibrary.authorizationStatus() == .authorized{
            completion()
        }
    }



func saveVideoToAlbum(_ outputURL: URL, _ completion: ((Error?) -> Void)?) {
        requestAuthorization {
            PHPhotoLibrary.shared().performChanges({
                let request = PHAssetCreationRequest.forAsset()
                request.addResource(with: .video, fileURL: outputURL, options: nil)
            }) { (result, error) in
                DispatchQueue.main.async {
                    if let error = error {
                        print(error.localizedDescription)
                    } else {
                        print("Saved successfully")
                    }
                    completion?(error)
                }
            }
        }
    }

Use of function

self.saveVideoToAlbum(/* pass your final url to save */) { (error) in
                        //Do what you want 
                    }

Don't forgot to import Photos and add Privacy - Photo Library Usage Description to your info.plist

Bonney answered 7/4, 2020 at 12:1 Comment(5)
Upvoted for including requestAuthorization method as well!Deepsea
The operation couldn’t be completed. (PHPhotosErrorDomain error -1.) "Showing Error"Penhall
Using PHAssetCreationRequest.forAsset() throws Objective-C exception for me. Had to use PHAssetChangeRequest.creationRequestForAssetFromVideo instead.Althorn
Hello, try this way it might helps: PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL:URL(fileURLWithPath:"#YourVideoPath")) }) { saved, error in if saved { // message here } else { //handle error here } }Bonney
PHPhotosErrorDomain error -1 may be happens due to earlier version of iOS or due to video quality settings in device.Bonney
T
3

deprecated as of iOS 9

1: import AssetsLibrary

import AssetsLibrary

2: Use this code to save video from url to camera library.

ALAssetsLibrary().writeVideoAtPathToSavedPhotosAlbum(outputFileURL, completionBlock: nil)
Tilley answered 14/9, 2015 at 9:35 Comment(1)
Watch out! AssetsLibrary was deprecated as of iOS 9Vannessavanni
R
1

A modern version using await/async & Swift 5.

import Foundation
import Photos

class PhotoLibrary {

    class func requestAuthorizationIfNeeded() async -> PHAuthorizationStatus {
        let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)

        if status == .notDetermined {
            return await PHPhotoLibrary.requestAuthorization(for: .readWrite)
        } else {
            return status
        }
    }

    enum PhotoLibraryError: Error {
        case insufficientPermissions
        case savingFailed
    }

    class func saveVideoToCameraRoll(url: URL) async throws {

        let authStatus = await requestAuthorizationIfNeeded()

        guard authStatus == .authorized else {
            throw PhotoLibraryError.insufficientPermissions
        }

        do {
            try await PHPhotoLibrary.shared().performChanges {
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
            }
        } catch {
            throw PhotoLibraryError.savingFailed
        }
    }
}

Then use it like this:

do {
    try await PhotoLibrary.saveVideoToCameraRoll(url: url)
} catch {
    // Handle error
}
Renick answered 8/6, 2023 at 7:56 Comment(0)
P
0

Just use it and paste your video's url:

PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in

    let createAssetRequest: PHAssetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(string: /* your url */)!)!
    createAssetRequest.placeholderForCreatedAsset

    }) { (success, error) -> Void in
        if success {

            //popup alert success
        }
        else {
           //popup alert unsuccess
        }
}
Palecek answered 7/10, 2018 at 10:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.