AbsterT
AbsterT

Reputation: 183

Swift Completion & Loop Issue

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?

Upvotes: 0

Views: 59

Answers (1)

ekscrypto
ekscrypto

Reputation: 3806

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...
    }
}

Upvotes: 1

Related Questions