35

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)
George Poulos
  • 539
  • 1
  • 6
  • 17
  • 3
    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. – matt Apr 07 '15 at 02:36
  • 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. – George Poulos Apr 07 '15 at 03:09
  • Nowadays NSURLSession is easier. And using a download task is trivial. Here's an example: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch24p834simpleHTTP/ch37p1088simpleHTTP/ViewController.swift 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! – matt Apr 07 '15 at 03:33

7 Answers7

67

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)
    }
}
drewster
  • 5,460
  • 5
  • 40
  • 50
Boris
  • 11,373
  • 2
  • 33
  • 35
  • 3
    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). – Christian Navelot Oct 12 '16 at 16:41
  • 4
    Wow, that Swift 3 solution looks familiar! – CodeBender May 23 '17 at 21:59
  • 2
    Yeah, whoever added the Swift 3 used yours or you both took it from the same place =) – Boris May 23 '17 at 22:04
  • 1
    Is this working only for local urls? Because it doesn't save a remote Url – Roi Mulia Jan 24 '18 at 09:58
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)
    }
}
CodeBender
  • 35,668
  • 12
  • 125
  • 132
  • 1
    CodeBender, will this work for remote url as well? Or I need to download it first and than use the local url? – Roi Mulia Jan 24 '18 at 09:58
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

Disha
  • 608
  • 7
  • 10
9

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

Mithilesh Kumar
  • 305
  • 4
  • 9
  • 1
    Upvoted for including requestAuthorization method as well! – Zez3 Jun 04 '20 at 13:18
  • 1
    The operation couldn’t be completed. (PHPhotosErrorDomain error -1.) "Showing Error" – Hemraj Jhariya Dec 16 '20 at 18:58
  • Using `PHAssetCreationRequest.forAsset()` throws Objective-C exception for me. Had to use `PHAssetChangeRequest.creationRequestForAssetFromVideo` instead. – Aleš Oskar Kocur Jun 08 '23 at 07:57
  • 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 } } – Mithilesh Kumar Jun 10 '23 at 06:46
  • PHPhotosErrorDomain error -1 may be happens due to earlier version of iOS or due to video quality settings in device. – Mithilesh Kumar Jun 10 '23 at 06:57
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)
Saqib Omer
  • 5,387
  • 7
  • 50
  • 71
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
        }
}
0

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
}
Aleš Oskar Kocur
  • 1,381
  • 1
  • 13
  • 29