24

The example for using Alamofire.download() works just fine, but there isn't any detail in how to access the resulting downloaded file. I can figure out where the file is and what it was named based on the destination I set and the original file request I made, but I would assume that there is a value I should be able to access to get the full final download path and file name in the response.

How do I access the name of the file and how do I know if it saved successfully? I can see delegate methods in the actual Alamofire code that appear to handle the delegate completion calls for the downloading process, but how do I / can I access the file details in the .response block?

Paul Bonneville
  • 1,905
  • 2
  • 13
  • 17

5 Answers5

27

Swift 2.1 and a little simpler:

var localPath: NSURL?
Alamofire.download(.GET,
    "http://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v",
    destination: { (temporaryURL, response) in
    let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let pathComponent = response.suggestedFilename
            
    localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
        return localPath!
    })
    .response { (request, response, _, error) in
        print(response)
        print("Downloaded file to \(localPath!)")
    }
)

Still hard to understand why they are using closures to set the destination path...

Community
  • 1
  • 1
Sam
  • 5,375
  • 2
  • 45
  • 54
  • 3
    If the destination file already exists, the rename/move will fail, so handling that case in the closure is handy, if you want to make use of the suggested filename. – penfold Jan 04 '16 at 06:04
  • @penfold true this saves me a lot of effort by writing the logic to replace the existing file with new file and restoring from temp file if the download fails. Im really happy that its a closure. – spaceMonkey Jul 11 '16 at 07:08
13

The example on the Alamofire readme file actually has the file path in it, so I grab it with a separate variable to use elsewhere in my code. It is not very elegant and I am hoping there is a way to get that info in the response, but for now, this is getting the job done:

var fileName: String?
var finalPath: NSURL?

Alamofire.download(.GET, urlToCall, { (temporaryURL, response) in

    if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {    

        fileName = response.suggestedFilename!
        finalPath = directoryURL.URLByAppendingPathComponent(fileName!)
        return finalPath!
    }

    return temporaryURL
})
    .response { (request, response, data, error) in

        if error != nil {
            println("REQUEST: \(request)")
            println("RESPONSE: \(response)")
        } 

        if finalPath != nil {
            doSomethingWithTheFile(finalPath!, fileName: fileName!)
        }
 }
Paul Bonneville
  • 1,905
  • 2
  • 13
  • 17
  • 1
    you can't access to finalPath or fileName variable from there – Gabriel Goncalves Mar 27 '15 at 15:42
  • 1
    If you want to access finalPath or fileName in the closure, you need to change the download call to "Alamofire.download(.GET, urlToCall, { [weak self] (temporaryURL, response) in" Then you can reference self.fileName and self.finalPath without causing a retain cycle. Also, you need to put this code in a class that's instantiated in a place where it won't go out of scope before the request completes. A simpler approach is to add a completion handling closure to the function where this code is, and pass finalPath to that completion handler. – Carl Smith Jul 07 '16 at 20:26
3

I spent about 8 hours looking for an answer to this one. The solution below works for me, and basically I chain to the download method to display the image. In the example below, I'm downloading the profile image of a user knowing his Facebook ID:

    let imageURL = "http://graph.facebook.com/\(FBId!)/picture?type=large"

    let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
        (temporaryURL, response) in

        if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
            var localImageURL = directoryURL.URLByAppendingPathComponent("\(self.FBId!).\(response.suggestedFilename!)")
            return localImageURL
        }
        return temporaryURL
    }

    Alamofire.download(.GET, imageURL, destination).response(){
        (_, _, data, _) in
            if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
                var error: NSError?
                let urls = NSFileManager.defaultManager().contentsOfDirectoryAtURL(directoryURL, includingPropertiesForKeys: nil, options: nil, error: &error)

                if error == nil {
                    let downloadedPhotoURLs = urls as! [NSURL]
                    let imagePath = downloadedPhotoURLs[0] // assuming it's the first file
                    let data = NSData(contentsOfURL: imagePath)
                    self.viewProfileImage?.image = UIImage(data: data!)
                }
            }
    }
esfoobar
  • 319
  • 1
  • 3
  • 10
2

Here is complete method to download file in different destination with progress

//MARK: Download methods

func downloadFile(reqType : RequestType,  urlParam: String,completionHandler: (Double?, NSError?) -> Void) {

    let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0])
    var downloadPath = documentsPath.URLByAppendingPathComponent("downloads")
    var isDirectory: ObjCBool = false
    if NSFileManager.defaultManager().fileExistsAtPath(downloadPath.path!, isDirectory: &isDirectory) {
        if(!isDirectory){
            do {
                try NSFileManager.defaultManager().createDirectoryAtPath(downloadPath.path!, withIntermediateDirectories: true, attributes: nil)
            }catch  {
                NSLog("Unable to create directory ")
            }
        }
    }else{
        do {
            try NSFileManager.defaultManager().createDirectoryAtPath(downloadPath.path!, withIntermediateDirectories: true, attributes: nil)
        }catch  {
            NSLog("Unable to create directory ")
        }

    }


    //get the url from GTM
    let urlString = self.getRequestedUrlFromIdentifier(reqType, param: urlParam)
    let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
    Alamofire.download(.GET, urlString, destination: { (temporaryURL, response) in

        let pathComponent = response.suggestedFilename
        downloadPath = downloadPath.URLByAppendingPathComponent(pathComponent!)
        if NSFileManager.defaultManager().fileExistsAtPath(downloadPath.path!) {
            do{
             try NSFileManager.defaultManager().removeItemAtPath(downloadPath.path!)
            }catch {

            }
        }
        return downloadPath
    })            .progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
            print(totalBytesRead)

            // This closure is NOT called on the main queue for performance
            // reasons. To update your ui, dispatch to the main queue.
            dispatch_async(dispatch_get_main_queue()) {
                print("Total bytes read on main queue: \(totalBytesRead)")
            }
        }
        .response { request, response, _, error in
            print(response)
            let originalPath = destination(NSURL(string: "")!, response!)
            if let error = error {
                completionHandler(500000.1 , nil)
                print("Failed with error: \(error)")
            } else {
                completionHandler(500000.0 , nil)
                print("Downloaded file successfully \(downloadPath)")
            }
    }

}
alok srivastava
  • 761
  • 8
  • 19
2

This answer from an Alamofire member seems to be the best answer:

let destination = Alamofire.Request.suggestedDownloadDestination(
    directory: .CachesDirectory,
    domain: .UserDomainMask
)

Alamofire.download(.GET, "http://www.adobe.com/devnet/acrobat/pdfs/pdf_open_parameters.pdf", destination: destination)
    .progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
        print(totalBytesRead)
    }
    .response { request, response, _, error in
        print(response)
        print("fileURL: \(destination(NSURL(string: "")!, response))")
}
Kristof Van Landschoot
  • 1,427
  • 1
  • 12
  • 30