Swift - Downloading video with downloadTaskWithURL
Asked Answered
E

2

13

I'm downloading a video thanks to downloadTaskWithURL and I'm saving it to my gallery with this code :

    func saveVideoBis(fileStringURL:String){

    print("saveVideoBis");

    let url = NSURL(string: fileStringURL);
    (NSURLSession.sharedSession().downloadTaskWithURL(url!) { (location:NSURL?, r:NSURLResponse?, e:NSError?) -> Void in

        let mgr = NSFileManager.defaultManager()

        let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0];

        print(documentsPath);

        let destination = NSURL(string: NSString(format: "%@/%@", documentsPath, url!.lastPathComponent!) as String);

        print(destination);

        try? mgr.moveItemAtPath(location!.path!, toPath: destination!.path!)

        PHPhotoLibrary.requestAuthorization({ (a:PHAuthorizationStatus) -> Void in
            PHPhotoLibrary.sharedPhotoLibrary().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(destination!);
                }) { completed, error in
                    if completed {
                        print(error);
                        print("Video is saved!");
                        self.sendNotification();
                    }
            }
        })
    }).resume()
}

It works perfectly fine on my simulator but on my iPad the video isn't saved even if the print("Video is saved!"); appears. Do you have any idea why ?

I also have that message appearing in my console

Unable to create data from file (null)

Edina answered 19/2, 2016 at 11:2 Comment(16)
The documentation for the completionHandler part of the downloadTaskWithURL method says "You must move this file or open it for reading before your completionHandler returns". Now, you seem to still be inside the completion handler at all times, but maybe (and I'm guessing here) you calling another method ('performChanges') inside your completionHandler is messing something up. Could you maybe try to copy the file to another location before you call performChanges?Rau
I'm relatively new to Swift, can you give me an example of how to do this please ?Edina
You need a sourceURL of type NSURL, thats your location then you need a destinationURL you could maybe take the location and append something to it, maybe let destination = NSURL(fileURLWithPath: location.absoluteString + "_dest"). Once you have that you need a fileManager of type NSFileManager, like so let fileManager = NSFileManager.defaultManager(). And then you are ready to move the file by saying fileManager.moveItemAtURL(location, toURL: destination). That method throws so you need to wrap that in a do...try...catch. Hope that makes senseRau
You can read more about the NSFileManager here: developer.apple.com/library/ios/documentation/Cocoa/Reference/… And this article from NSHipster is worth a read too: nshipster.com/nsfilemanagerRau
I did it and edited my code, I fall directly in the error caseEdina
In the error case of the moveItemAtURL you mean? OK, what does it say then if you do a print of the error? do {try something} catch let error { print("error goes here: \(error)")}Rau
Try '(BOOL)createDirectoryAtURL:(NSURL *)url withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSString *, id> *)attributes error:(NSError * _Nullable *)error' If YES, this method creates any non-existent parent directories as part of creating the directory in url. If NO, this method fails if any of the intermediate parent directories does not exist.... set it to yes.Sulphurous
I totally change my code on it works on my emulator but it doesn't work on my iPad....Edina
@MelanieJourne you should better name your variables to improve readability. you should also work with url and use its path property if neededRodmun
fileURL it is totally misleading name. It is a string (weblink) and it is not a local fileRodmun
I change the variable name. Also I didn't find my answer in your link :/Edina
You are not suppose to find the answer. You need to understand the difference between absoluteString and path. If you need a url for a local file just use NSURL initialiser fileURLWithPath (note that absoluteString has file:// prefix and you can't include that when using NSURL fileURLWithPath initialiser)Rodmun
Yes, I logged everything to see the content variable and understand this. But it's not a duplicate of my question because your link won't explain me why my code works on simulator and not on a real device...Edina
Yes it was definitely a duplicate as you were trying to use absoluteString instead of path using NSURL fileURLWithPath initializer. I can reopen your question but I think it is all about when use one or the otherRodmun
If it's about that, why does it work on the simulator of Xcode and not on my device ?Edina
I am taking about your original issue. You should have opened a new question. I will reopen it anyway and leave the duplicated link here #16177411Rodmun
R
30

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")
    }

}
Rodmun answered 19/2, 2016 at 17:5 Comment(12)
It works perfectly fine thanks you ! :) Only one little downside : when I download a movie, then delete it, then try to download it again, it tells me that "the file already exists at destination url".Edina
You can remove that condition and save it with a new nameRodmun
@LeoDabus Can you please tell me how i can use NSURLSessionDownloadDelegate methods with this code? because its methods did't works with this code?Unwitnessed
is there a way to fetch that specific asset from the photos library after downloading and saving it ?Oblivion
@LeoDabus : I used same code to save a .m3u8 file. But, when try to save the file it getting error , ' The operation couldn’t be completed. (Cocoa error -1.)'Wun
@LeoDabus how to download pdf in same way?Katiakatie
#47140814Rodmun
@LeoDabus I need to save the downloaded pdf into the files folder. I tried the above answer. but it shows "no preview to show"Katiakatie
Maybe your link is not https. I can only guess It would be easier for you to open a new question and put your issue thereRodmun
Received this error: Optional(Error Domain=PHPhotosErrorDomain Code=3302 "(null)")Tullusus
Your url points to your photo asset you need to use the framework method to fetch its dataRodmun
@Tullusus check this post https://mcmap.net/q/590529/-swift-photo-library-accessRodmun
D
0

If you want to download Video from URL but don't want to store it on Gallery. following code works for me...

 func createDownloadTask(videoURL: String,index: Int) {
    let downloadRequest = NSMutableURLRequest(url: URL(string: videoURL)!)
    let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
    self.downloadTask = session.downloadTask(with: downloadRequest as URLRequest, completionHandler: { (url, response, error) in
        print("asdasd")
        if error != nil{
            if super.reachability.connection == .none{
                self.showalert(msg: error?.localizedDescription ?? "")
            }else{
                self.downloadTask?.resume()
            }
        }

        guard let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
        // create a deatination url with the server response suggested file name
        let destinationURL = documentsDirectoryURL.appendingPathComponent(response?.suggestedFilename ?? "")

        do {
            if url != nil{
            try FileManager.default.moveItem(at: url!, to: destinationURL)
            }

            }

        } catch { print(error) }

    })
    self.downloadTask!.resume()
}
Duong answered 22/5, 2019 at 8:13 Comment(3)
which type of self.downloadTask?Hux
var downloadTask : URLSessionDownloadTask?Duong
How do you play the downloaded video?Dobbin

© 2022 - 2024 — McMap. All rights reserved.