Search code examples
iosswift3nsurlsessionnsurlsessiondownloadtask

Swift downloadTask with request file download not working


I'm trying to download a file from a server, by sending the id of the file. I tried several things but the file is getting downloaded as CFNetworkDownload.tmp file.

I want it to save as the file that exists. The file type can be PNG,JPEG,PDF,DOCX,PPTX,XLSX. Tried many things but in vain. I'm sure it must be something simple i'm missing to understand here

Tried the below. Difference being in most of the examples, the file name is in the URL. But I send id and get file in response.

How to download file in swift?

How To Download Multiple Files Sequentially using NSURLSession downloadTask in Swift

Below is my code.

func downloadFile(id : String, fileName : String) -> Void {


    let session = URLSession.shared
    let url = URL(string: qaDownloadURL+id)!

    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
        if let tempLocalUrl = tempLocalUrl, error == nil {
            // Success
            if let statusCode = (response as? HTTPURLResponse)?.statusCode {
                print("Success: \(statusCode)")
            }

            do {



//                    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
//                    
//                    self.savePath = documentsUrl!.absoluteString + "/" + fileName
//                    
//                    let fileURL = URL(fileURLWithPath: self.savePath)
//                    
//                    let dataFromURL = NSData(contentsOf: tempLocalUrl)
//                    dataFromURL?.write(to: fileURL, atomically: true)

                var documentsDirectory: String?

                let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)

                if paths.count > 0
                {
                    documentsDirectory = paths.first!
                }

                self.savePath = documentsDirectory!// + "/" + fileName

                let fileURL = URL(fileURLWithPath: self.savePath)


                let dataFromURL = NSData(contentsOf: tempLocalUrl)
                dataFromURL?.write(to: fileURL, atomically: true)


              //  try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)


                DispatchQueue.main.async {

                    let documentController = UIDocumentInteractionController.init(url: fileURL)
                    documentController.delegate = self
                    documentController.presentPreview(animated: true)

                }   

            } catch (let writeError) {
                print("error writing file \(self.savePath) : \(writeError)")
            }

        } else {
            print("Failure: %@", error?.localizedDescription);
        }
    }
    task.resume()

}

Solution

  • You cannot write data into a location which represents a directory, you need to specify the full path including the file name.

    Using modern URL related API you can replace the entire do block with

        do {
                let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
                let fileURL = documentFolderURL.appendingPathComponent(fileName)
                try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)
    
                DispatchQueue.main.async {
    
                    let documentController = UIDocumentInteractionController.init(url: fileURL)
                    documentController.delegate = self
                    documentController.presentPreview(animated: true)
    
                }    
            }
    

    or use URLSessionDataTask which returns the raw data rather than downloading the file to a temporary location and save the Data directly for example

    let task = session.dataTask(with: request) { (data, response, error) in
        guard error == nil else {
            print(error!)
            return
        }
        // Success
        if let statusCode = (response as? HTTPURLResponse)?.statusCode {
            print("Success: \(statusCode)")
        }
    
        do {
            let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            let fileURL = documentFolderURL.appendingPathComponent(fileName)
            try data!.write(to: fileURL)
    
            DispatchQueue.main.async {
    
                let documentController = UIDocumentInteractionController.init(url: fileURL)
                documentController.delegate = self
                documentController.presentPreview(animated: true)
    
            }
    
        } catch  {
            print("error writing file \(fileName) : \(error)")
        }
    }
    task.resume()
    

    If this does not work the error is related to somewhere else.