Harry Day
Harry Day

Reputation: 408

Returning from inside DispatchQueue Swift

I am building a function that takes a CLLocation parameter and returns a Weather Forecast of type DarkSkyResponse which I have defined myself.

public func getForecast(at location: CLLocation) -> DarkSkyResponse {
    let request = DarkSkyRequest(key: "KEYGOESHERE")
    let point = DarkSkyRequest.Point(location.coordinate.latitude, location.coordinate.longitude)

    guard let url = request.buildURL(point: point) else {
        preconditionFailure("Failed to construct URL")
    }

    let task = URLSession.shared.dataTask(with: url) {
        data, response, error in

        DispatchQueue.main.async {
            guard let data = data else {
                fatalError("No Data Recieved")
            }
            guard let forecast = DarkSkyResponse(data: data) else {
                fatalError("Decoding Failed")
            }
            return forecast
        }
    }
    task.resume()
}

Everything works perfectly and I just want to return the forecast variable found inside the DispatchQueue. When I tried to do this I was met with the following error: Unexpected non-void return value in void function I believe that this is because I am returning inside of the DispatchQueue.

To try and fix this I moved the data task and decoding process into it's own function that has a completion handler. When I call the function I just have the closure set to return the output as so:

public func getForecast(at location: CLLocation) -> DarkSkyResponse {
    let request = DarkSkyRequest(key: "KEYGOESHERE")
    let point = DarkSkyRequest.Point(location.coordinate.latitude, location.coordinate.longitude)

    guard let url = request.buildURL(point: point) else {
        preconditionFailure("Failed to construct URL")
    }

    func getDataAndDecode(from url: URL, completionBlock: @escaping (DarkSkyResponse) -> Void) -> Void {
        let task = URLSession.shared.dataTask(with: url) {
            data, response, error in

            DispatchQueue.main.async {
                guard let data = data else {
                    fatalError("No Data Recieved")
                }
                guard let forecast = DarkSkyResponse(data: data) else {
                    fatalError("Decoding Failed")
                }
                completionBlock(forecast)
            }
        }
        task.resume()
    }

    getDataAndDecode(from: url) { (output) in
        return output
    }
}

Now I get a warning saying: Expression of type 'DarkSkyResponse' is unused. I am doing this the correct way or is there a more elegant way of returning the value I want?

Upvotes: 1

Views: 1831

Answers (1)

Nanda Julianda Akbar
Nanda Julianda Akbar

Reputation: 66

As for now, swift doesn’t support yet for async await style (similar to javascript), that means you can’t return directly from async operation. You better put the callback(completionBlock) argument at getForecast method. And it would be nicely if the callback also handle the error case(e. g. onSuccess and onError). And don’t forget to set getForecast method as void method.

So it would be like these :


func getForecast(at location: CLLocation, onSuccess: (DarkSkyResponse) -> (), onError: (Error) -> ()) {

...
}

Upvotes: 2

Related Questions