0

Trying to download a PKG file from one of three urls. The logic basically finds the latency of a download from each download url and sets the final download url to the host with the lowest latency.

import Cocoa
import Alamofire

// Create my object via Struct
struct Package {
    var latency: Double?
    var name: String?
    var statuscode: Int?
    var download: Bool?
    var downloadUrl: String? 
}
// Download the package from the provided download url and return the object
func getPKG(pkgName: String, dpUrl: String, completion: @escaping (Package) -> (Package)) {
    let url = URL(string: "\(dpUrl)\(pkgName)")
    let parameters: Parameters = ["foo":"bar"]
    Alamofire.download(url, method: .get, parameters: parameters, encoding: JSONEncoding.default, to: destination)
        .downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
            debugPrint("Download Progress...: \(progress.fractionCompleted)")
        }
        .validate(statusCode: 200..<399)
        .response { response in
            debugPrint(response.response!)
            debugPrint(response.response!.statusCode)
            debugPrint(response.timeline.latency)
            let dlObject = Package(latency: response.timeline.latency, name: pkgName, statuscode: response.response?.statusCode, download: true, downloadUrl: dpUrl)
            completion(dlObject)
    }

}

var share_response = [String: Double]()
var package_sources: NSArray! = ["https://www.jss1.com/Share", "https://www.jss2.com/Share", "https://www.jss3.com/Share"]
let package_names: String = ["Dummy1.pkg", "Dummy2.pkg", "Dummy3.pkg"]
// Loop through the package sources and find the one with 
// the lowest latency.
for share_url in package_sources {
    getPKG(pkgName: "Dummy.pkg", dpUrl: share_url, completion: {
        dlObject in
        if dlObject.latency != nil {
            share_response[share_url] = dlObject.latency
        } else {
            debugPrint("nothing yet")
        }
        return dlObject
    })
}

let final_download_url = share_response.min { a, b in a.value < b.value }

//  Here is where it breaks and responds with nil

for package in package_names {
    let download_url = URL(string: final_download_url + package)
    Download commands here...
}

This is done by looping through each download url and populating a dictionary with the key as the url and the value as the latency. When the script moves on to download from the "fastest" download url, it fails with nil.

I'm assuming that's because the script is moving on while the completion handler is still running and nothing is in the dictionary yet, but how would I address this?

AbsterT
  • 173
  • 4
  • 1
    `(Package) -> (Package)` cannot work. You need `DispatchGroup` to synchronize the network requests in the loop. By the way the type annotation `: NSArray!` is pretty bad. It's neither `NSArray` nor optional. Never annotate types the compiler can infer much much better. And this is not Javascript: Variable names are supposed to be *camelCased* – vadian Aug 17 '18 at 16:48
  • @vadian I put this together pretty quick and redacted a lot of company specific stuff, so I wasn't too concerned about the variable names. Wanted to make it as universal as possible. I really appreciate your answer/comments though and those are excellent points I will take back. can you give an example of how it would look with a DispatchGroup? – AbsterT Aug 17 '18 at 16:54
  • 1
    I just wrote an answer to a [related question](https://stackoverflow.com/questions/51899792/synchronous-request-using-alamofire/51899933#51899933) – vadian Aug 17 '18 at 16:59
  • @vadian I've looked at that other post and started editing the code to test, but one question regarding the group.notify part. Where would I put that part in the code I've posted? – AbsterT Aug 17 '18 at 17:37

1 Answers1

1

Based on the answer from @vadian at Synchronous request using Alamofire

...

let group = DispatchGroup()
var share_response = [String: Double]()
var package_sources: NSArray! = ["https://www.jss1.com/Share", "https://www.jss2.com/Share", "https://www.jss3.com/Share"]
let package_names: String = ["Dummy1.pkg", "Dummy2.pkg", "Dummy3.pkg"]
// Loop through the package sources and find the one with 
// the lowest latency.
for share_url in package_sources {
    group.enter()
    getPKG(pkgName: "Dummy.pkg", dpUrl: share_url, completion: {
        group.leave()
        dlObject in
        if dlObject.latency != nil {
            share_response[share_url] = dlObject.latency
        } else {
            debugPrint("nothing yet")
        }
        return dlObject
    })
}

group.notify(queue: DispatchQueue.main) {    
    let final_download_url = share_response.min { a, b in a.value < b.value }

    //  Here is where it breaks and responds with nil

    for package in package_names {
        let download_url = URL(string: final_download_url + package)
        Download commands here...
    }
}
ekscrypto
  • 3,718
  • 1
  • 24
  • 38
  • @vadian & ekscrypto, thanks for all your help. This was what I needed. I can now successfully find the lowest latency from the provided urls, pick the fastest host, then download packages from that specific host accordingly. Thank you again! – AbsterT Aug 17 '18 at 19:00