6

I want to save a video chosen from UIImagePickerController to the Photos album. After that i need to fetch the URL to the saved path. I saw some pre-existing questions which answer with use of ALAssetsLibrary which is now deprecated. Like : Save video on the gallery and store the path to the video

Can someone please guide me to use Photo's framework and achieve the above desired result. Kindly correct me if I am wrong anywhere.

Thanks!!

Community
  • 1
  • 1
Ankit Kumar Gupta
  • 3,994
  • 4
  • 31
  • 54
  • 1
    please refer this one you will get an idea how to deal with PHAsset https://github.com/SanjeetVerma/UIImagePicker-PHAsset-swift/blob/master/PhotoGalleryDemo/ViewController.swift – Bhupat Bheda Apr 25 '17 at 10:48
  • Bro your example is nice. But still i am not able to get the path for the video that is stored to the gallery. – Ankit Kumar Gupta Apr 25 '17 at 11:03
  • 1
    You can get the path for the video in that example like let videoPath = info[UIImagePickerControllerMediaURL] as! NSURL just check in that example – Bhupat Bheda Apr 25 '17 at 11:07
  • 1
    that is the link to the temporary directory. I want the url for the asset stored in Gallery . – Ankit Kumar Gupta Apr 25 '17 at 11:11
  • Bro this is not temporary path i am picking the video using UIimagepicker controller which gives the path of gallery not mine custom folder path after getting the path then i adding in my folder so that path is gallery assest url – Bhupat Bheda Apr 25 '17 at 11:17
  • bro when u use the camera to record a video then when didFinishPickingMediaWithInfo is called you get the temp folders location. So now u use the same location to save file to gallery. But after that were to get the link which is now accessed with the gallery – Ankit Kumar Gupta Apr 25 '17 at 11:26
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142604/discussion-between-bhupat-bheda-and-ankit-kumar-gupta). – Bhupat Bheda Apr 25 '17 at 11:29

6 Answers6

18

This worked for me perfectly.

Swift 3.1 ->

PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url!)
}) { saved, error in
            if saved {
                let fetchOptions = PHFetchOptions()
                fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]

                // After uploading we fetch the PHAsset for most recent video and then get its current location url

                let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).lastObject 
                PHImageManager().requestAVAsset(forVideo: fetchResult!, options: nil, resultHandler: { (avurlAsset, audioMix, dict) in
                    let newObj = avurlAsset as! AVURLAsset
                    print(newObj.url) 
                    // This is the URL we need now to access the video from gallery directly.
                    })
            }
}
Jeri P.M
  • 399
  • 5
  • 15
Ankit Kumar Gupta
  • 3,994
  • 4
  • 31
  • 54
7

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

Then use this code to store video

    PHPhotoLibrary.shared().performChanges({
        PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileURL)
    }) { saved, error in
        if saved {
            let fetchOptions = PHFetchOptions()
            fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

            let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
            // fetchResult is your latest video PHAsset
            // To fetch latest image  replace .video with .image
        }
    }

To get url from PHAsset, refer to this question's answer

Community
  • 1
  • 1
Surjeet Singh
  • 11,691
  • 2
  • 37
  • 54
3

If you want to fetch the saved element (not using a sort descriptor with date order), you can do it this way:

var changeRequest: PHAssetChangeRequest?
var blockPlaceholder: PHObjectPlaceholder?

PHPhotoLibrary.shared().performChanges({
            changeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url!)
            blockPlaceholder = changeRequest?.placeholderForCreatedAsset
}) { saved, error in
            if saved {
                guard let placeholder = blockPlaceholder else {
                    return
                }
                let fetchOptions = PHFetchOptions()
                let fetchResult:PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [placeholder.localIdentifier], options: fetchOptions)
                if let asset = fetchResult.firstObject {
                    //here you have the PHAsset
                } 
            }
}
Bisca
  • 6,380
  • 2
  • 19
  • 32
1

Try like this!

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]){
if mediaType.isEqualToString(kUTTypeMovie as NSString as String) || mediaType.isEqualToString(kUTTypeVideo as NSString as String){
    let videoPath = info[UIImagePickerControllerMediaURL] as! NSURL // Path of video Url
    PHPhotoLibrary.sharedPhotoLibrary().performChanges({
    let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoPath)
    let assetPlaceholder = createAssetRequest?.placeholderForCreatedAsset
    let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection, assets: self.photosAsset)
            albumChangeRequest!.addAssets([assetPlaceholder!])
       }, completionHandler: { (success, error) in
                    NSLog("Adding video to Library ->%@", (success ? "Success" : "Error"))
                    picker.dismissViewControllerAnimated(true, completion: nil)
            })
      }
}
Bhupat Bheda
  • 1,968
  • 1
  • 8
  • 13
1

Here's the answer for Objective-C

Code Sample:

 [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:[NSURL URLWithString:urlOfFile]];
} completionHandler:^(BOOL success, NSError *error) {
                                      if (success)
                                      {
                                          PHFetchOptions *fetchOptions = [[PHFetchOptions alloc]init];
                                          fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:false]];
                                          PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeVideo options:fetchOptions];
                                          PHAsset *lastAsset = [fetchResult lastObject];
                                          [[PHImageManager defaultManager] requestAVAssetForVideo:lastAsset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
                                              //here's the url to the file
                                              NSURL *url = (NSURL *)[(AVURLAsset *)asset URL];


                                      }else{
                                          NSLog(@"%@",error.description);
                                      }
                                  }];
halfer
  • 19,824
  • 17
  • 99
  • 186
Harjot Singh
  • 6,767
  • 2
  • 38
  • 34
0

I do this by creating extension of the URL Class

extension URL {

    func saveVideo( success:@escaping (Bool,URL?)->()){


        URLSession.shared.downloadTask(with: URLRequest(url: self)) { (url, response, error) in

            let mgr = FileManager.default

            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]


            let destination = URL(fileURLWithPath: String(format: "%@/%@", documentsPath, "video.mp4"))


            try? mgr.moveItem(atPath: /url?.path, toPath: /destination.path)

            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: destination)
            }) { completed, error in
                if completed {
                    print("Video is saved!")
                    success(true,destination)
                }
                if error != nil{
                    success(false,nil)
                }
            }
        }.resume()
    }
}

when you want to save and play you can use this in ViewController as

guard let urlToDownload = URL(string: videoURLStringToDownload) else { return }

  urlToDownload.saveVideo(success: { (isSaved,url) in           
    guard let videoURL = url, isSaved else { return }

        let player = AVPlayer(url: videoURL)
          let playerViewController = AVPlayerViewController()
           playerViewController.player = player
          self.present(playerViewController, animated: true) {
                            playerViewController.player?.play()
          }
    })

import AVKit for playing videos through AVPlayerViewController

import Photos and PhotosUI for saving Video with PHAsset.

Hope it helps!

Agent Smith
  • 2,873
  • 17
  • 32
  • You are saving the video to documents directory first then creating the save request to gallery. To avoid memory consumption of app only I wanted to save it gallery and then refer the video from there. – Ankit Kumar Gupta Apr 25 '17 at 11:14
  • 1
    Okay!!! for that you can refer to this [class](https://github.com/zakkhoyt/PHAsset-Utility) for saving and retrieving directly from Albums. – Agent Smith Apr 25 '17 at 12:31