Swift - Download a Video from Distant Url and Save It in an Photo Album

Swift - Download a video from distant URL and save it in an photo album

Update

Wanted to update the answer for Swift 3 using URLSession and figured out that the answer already exists in related topic here. Use it.

Original Answer

The code below saves a video file to Camera Roll. I reused your code with a minor change - I removed let fileName = videoImageUrl; because it leads to incorrect file path.

I tested this code and it saved the asset into camera roll. You asked what to place into creationRequestForAssetFromVideoAtFileURL - put a link to downloaded video file as in the example below.

let videoImageUrl = "http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"

DispatchQueue.global(qos: .background).async {
if let url = URL(string: urlString),
let urlData = NSData(contentsOf: url) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
let filePath="\(documentsPath)/tempFile.mp4"
DispatchQueue.main.async {
urlData.write(toFile: filePath, atomically: true)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
}) { completed, error in
if completed {
print("Video is saved!")
}
}
}
}
}

Download and Save Video to Camera Roll

Unfortunately there is a bug related to UISaveVideoAtPathToSavedPhotosAlbum and the format mp4, which is the format used by Instagram.

There is a helper method called UIVideoAtPathIsCompatibleWithSavedPhotosAlbum to help indicate whether a video is compatible with the method UISaveVideoAtPathToSavedPhotosAlbum. This returns false for the video downloaded from Instagram.


Luckily it is still possible to store the videos into the camera roll. This is possible using ALAssetsLibrary. I've tried to take your sample code and adapt it to use ALAssetsLibrary, hopefully this can help you to get it working.

import AssetsLibrary

...
...

func downloadVideoToCameraRoll() {

// Local variable pointing to the local file path for the downloaded video
var localFileUrl: String?

// A closure for generating the local file path for the downloaded video. This will be pointing to the Documents directory with a unique UDID file name.
let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
(temporaryURL, response) in

if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
let finalPath = directoryURL.URLByAppendingPathComponent("\(NSUUID()).\(response.suggestedFilename!)")
localFileUrl = finalPath.absoluteString
return finalPath
}

return temporaryURL
}

// The media post which should be downloaded
let postURL = NSURL(string: "https://api.instagram.com/v1/media/" + "952201134785549382_250131908" + "?access_token=" + InstagramEngine.sharedEngine().accessToken)!

// Then some magic happens that turns the postURL into the videoURL, which is the actual url of the video media:
let videoURL = NSURL(string: "https://scontent.cdninstagram.com/hphotos-xfp1/t50.2886-16/11104555_1603400416544760_416259564_s.mp4")!

// Download starts
let request = Alamofire.download(.GET, videoURL, destination)

// Completion handler for the download
request.response { (request, response, data, error) -> Void in
if let path = localFileUrl {
let isVideoCompatible = UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)
println("bool: \(isVideoCompatible)") // This logs out "bool: false"

let library = ALAssetsLibrary()

library.writeVideoAtPathToSavedPhotosAlbum(NSURL(string: path), completionBlock: { (url, error) -> Void in
// Done! Go check your camera roll
})
}
}
}

Best way to save photo from URL, swift

This should work for you. Used this answer from Leo Dabus for the getDataFromUrl method to get the data from the URL:

Don't forget to add the key Privacy - Photo Library Additions Usage Description to your Info.plist with a description to explain the user why you need access to the photo library.

Sample Image

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

@IBOutlet var imageView: UIImageView!

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}


func getDataFromUrl(url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}


@IBAction func savePhoto(_ sender: UIButton) {

let yourImageURLString = "https://scontent-frx5-1.cdninstagram.com/t51.2885-15/e35/22794197_139950336649166_440006381429325824_n.jpg"

guard let yourImageURL = URL(string: yourImageURLString) else { return }

getDataFromUrl(url: yourImageURL) { (data, response, error) in

guard let data = data, let imageFromData = UIImage(data: data) else { return }

DispatchQueue.main.async() {
UIImageWriteToSavedPhotosAlbum(imageFromData, nil, nil, nil)
self.imageView.image = imageFromData

let alert = UIAlertController(title: "Saved", message: "Your image has been saved", preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default)
alert.addAction(okAction)
self.present(alert, animated: true)
}
}

}
}

Save video to storage without having it show up in photos gallery (Swift iOS)

Saving a video onto the storage will not display in the Photo Gallery, in iOS/iPad OS the Photo Gallery requires another set of permissions and you have to specifically save in the Foto Gallery.

I would recommend to use your app isolated storage as the user can not see the files with 3rd apps or even Apple's File Manager.

For that take a look into FileManager class witch allows you to save files onto the disk.

I leave this tutorial as starting point iOS FileManager in Swift

iOS: Download video from url and save in gallery......?

Use this code.I hope it will work properly.:

    -(void)DownloadVideo
{
//download the file in a seperate thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Downloading Started");
NSString *urlToDownload = @"http://www.somewhere.com/thefile.mp4";
NSURL *url = [NSURL URLWithString:urlToDownload];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"thefile.mp4"];

//saving is done on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[urlData writeToFile:filePath atomically:YES];
NSLog(@"File Saved !");
});
}

});
}

Swift - Downloading video with downloadTaskWithURL

Please check comments through the code:

Xcode 8 • Swift 3

import UIKit
import Photos

class ViewController: UIViewController {

func downloadVideoLinkAndCreateAsset(_ videoLink: String) {

// use guard to make sure you have a valid url
guard let videoURL = URL(string: videoLink) else { return }

guard let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }

// check if the file already exist at the destination folder if you don't want to download it twice
if !FileManager.default.fileExists(atPath: documentsDirectoryURL.appendingPathComponent(videoURL.lastPathComponent).path) {

// set up your download task
URLSession.shared.downloadTask(with: videoURL) { (location, response, error) -> Void in

// use guard to unwrap your optional url
guard let location = location else { return }

// create a deatination url with the server response suggested file name
let destinationURL = documentsDirectoryURL.appendingPathComponent(response?.suggestedFilename ?? videoURL.lastPathComponent)

do {

try FileManager.default.moveItem(at: location, to: destinationURL)

PHPhotoLibrary.requestAuthorization({ (authorizationStatus: PHAuthorizationStatus) -> Void in

// check if user authorized access photos for your app
if authorizationStatus == .authorized {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: destinationURL)}) { completed, error in
if completed {
print("Video asset created")
} else {
print(error)
}
}
}
})

} catch { print(error) }

}.resume()

} else {
print("File already exists at destination url")
}

}

override func viewDidLoad() {
super.viewDidLoad()
downloadVideoLinkAndCreateAsset("https://www.yourdomain.com/yourmovie.mp4")
}

}


Related Topics



Leave a reply



Submit