BSUK
BSUK

Reputation: 745

Download a file to a temp location in Swift 4 (Mac OS) like with curl

I'm writing a CLI tool in Swift 4 for MacOS. If I "cheat" and use a shell function, then the curl command, I get exactly the result I'm after:

func shell(_ args: String...) -> Int32 {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    task.launch()
    task.waitUntilExit()
    return task.terminationStatus
}

shell("/usr/bin/curl", "-OL", "http://mywebserver/myfile.whatever")

How do I do the same thing natively in Swift 4? I tried using the just library: https://github.com/dduan/Just but I can't figure out how to simply get it to download a file from a URL to a specific location on the file system. I've also found various solutions for Swift 3 and 5, and iOS which obviously don't work for me.

Upvotes: 2

Views: 960

Answers (2)

vadian
vadian

Reputation: 285170

Simple example: Due to the asynchronous behavior of URLSession you have to start the runloop after resuming the data task and stop it in the completion handler.

let runLoop = CFRunLoopGetCurrent()
let url = URL(string: "http://mywebserver/myfile.whatever")!

let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
    let returnValue : Int32
    if let error = error {
        fputs("\(error.localizedDescription)\n", stderr)
        returnValue = EXIT_FAILURE
    } else {
        let result = String(data:data!, encoding:.utf8)!
        fputs("\(result)\n", stdout)
        returnValue = EXIT_SUCCESS
    }
    CFRunLoopStop(runLoop)
    exit(returnValue)
}

dataTask.resume()
CFRunLoopRun()

The code returns the data in stdout. You can write the data directly to disk, replace

let result = String(data:data!, encoding:.utf8)!
fputs("\(result)\n", stdout)

with

 let fileURL = URL(fileURLWithPath: "/Users/me/path/to/file.ext")
 do {
    try data!.write(to: fileURL)
 } catch { 
   fputs("\(error.localizedDescription)\n", stderr)
 }

Upvotes: 1

Ankur Lahiry
Ankur Lahiry

Reputation: 2315

You can download using downloadTask

  1. Create session configuration
  2. Create URLSession with this configuration
  3. Apply download task
  4. When the download is done successfully, copy the data to the destination
let documentsUrl:URL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.jpg")

    //Create URL to the source file you want to download
let fileURL = URL(string: "https://en.wikipedia.org/wiki/Saint_Martin#/media/File:Saint_martin_map.PNG")

    let sessionConfig = URLSessionConfiguration.default
    let session = URLSession(configuration: sessionConfig)

    let request = URLRequest(url:fileURL!)

    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("Successfully downloaded. Status code: \(statusCode)")
            }

            do {
                try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
            } catch (let writeError) {
                print("Error creating a file \(destinationFileUrl) : \(writeError)")
            }

        } else {
            print("Error took place while downloading a file. Error description: %@", error?.localizedDescription);
        }
    }
    task.resume()

  }

Upvotes: 0

Related Questions