Search code examples
iosiphoneswift2alamofire

Download fails when on first use or app goes in background


Alamofire 3.5.0, Swift2.2

I'm downloading ZIP files with the Alamofire download method, I've noticed that when I'm starting download process and apps goes background than the download fails with following error:

----------------------
error Optional(Error Domain=NSCocoaErrorDomain Code=4
    "“CFNetworkDownload_pZ56nc.tmp” couldn’t be moved to “courses”
    because either the former doesn't exist, or the folder containing
    the latter doesn't exist." UserInfo=
    {NSSourceFilePathErrorKey=
/private/var/mobile/Containers/Data/Application/[UUID]/tmp/CFNetworkDownload_pZ56nc.tmp, 
    NSUserStringVariant=(
        Move
    ),     
    NSDestinationFilePath=
/var/mobile/Containers/Data/Application/[UUID]/Library/courses/course_302.zip, 
    NSFilePath=
/private/var/mobile/Containers/Data/Application/[UUID]/tmp/CFNetworkDownload_pZ56nc.tmp, 
    NSUnderlyingError=0x13f52f990 {Error Domain=NSPOSIXErrorDomain
    Code=2 "No such file or directory"}})
----------------------

this is the code to download a file:

//...
var userLibraryPath:String = {
    return NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0]
}()

//...
let _coursePath:NSURL = NSURL(string: "file://\(userLibraryPath)/)")!
//...
let zipURL = _coursePath.URLByAppendingPathComponent("course_\(courseId).zip")
        //if file exists destroy it
        if let zipPath = zipURL?.path where NSFileManager.defaultManager().fileExistsAtPath(zipPath) {
            do {
                try NSFileManager.defaultManager().removeItemAtPath(zipPath)
            } catch let error as NSError {
                print(error)
            }
        }
        //
        let downloadRequest = Alamofire.download(Router.download(courseId), destination: { (url:NSURL, urlResponse:NSHTTPURLResponse) -> NSURL in
            //
            return zipURL!
            //
        }).progress({ (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
            //
            let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)

            dispatch_async(GlobalMainQueue, {
                self.notifyDownloadProgress(courseId, progress: progress)
            })

        }).response(completionHandler: { (request:NSURLRequest?, response:NSHTTPURLResponse?, data:NSData?, error:NSError?) in
            self.removeFromQueue(courseId)
            print("response")
            print("----------------------")
            print("error \(error)")
            print("----------------------")
            //here I would try to extract it
})

UPDATE I've just tested on iPhone 5 fresh install of the app and it doesn't have to go to background (e.g. via home button) to fail, it fails on the very first load (and any subsequent) untill after the app is killed and reopened.

Why is the "/private" bit added to the path? What am I doing wrong here?


Solution

  • And indeed it was a "No such file or directory" error.

    When I've added:

    //
    let downloadRequest = Alamofire.download(Router.download(courseId), destination: { (url:NSURL, urlResponse:NSHTTPURLResponse) -> NSURL in
        let course = zipURL!.URLByDeletingLastPathComponent!.path!
        let fm = NSFileManager.defaultManager()
        var isDir:ObjCBool = false
        if(fm.fileExistsAtPath(path, isDirectory: &isDir) == false){
           //doesnt exist
           do {
               try fm.createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
           } catch let error as NSError {
               //
               print(error)
           }
        }
    
        return zipURL!
        //
     })