Search code examples
jsonswifthttphttprequestcompletionhandler

http request completion handler and closure firing before completion of the response


So the issue I have currently is that the closure is firing before the completion of the request response.

I have implemented a completion handler and closure, I am unsure if I have done this incorrectly or if the method in which I am calling the functions is incorrect.

here is the request containing the completion handler.

func ElevationRequest(listOfPointsToQuery: Array<CLLocationCoordinate2D>, success:Bool,completeHandler: (Bool) -> Void){
        var processedQueryPoints = Array<String>() //points which have been ordered based on the execution order are stored here
        var newStringCoordinate = String() // the next coordinates are stored here temporarily
        var finalStringConvertedCoordinates = String()
        var jsonResponse = String()
        for i in 0..<listOfPointsToQuery.count{ //go through all listed points to be queried within the http request sent to googles elevation api - returned as a json format dictionary
            var latHolder = String(listOfPointsToQuery[i].latitude)
            var lonHolder = String(listOfPointsToQuery[i].longitude)
            newStringCoordinate = latHolder + "," + lonHolder
            processedQueryPoints.append(newStringCoordinate)
                    }
        finalStringConvertedCoordinates = processedQueryPoints.joined(separator: "%7C") //google documentation demonstrates the use of the pipe ("|") - using pipe causes errors and the function will not.. function. "%7C" is used in its place
        let apiKey = "AIzaSyAPqcl47sO5eJIkdl46Ww-uJLgv_7aJq3I"
        print(finalStringConvertedCoordinates)
        struct ElevationResponse: Decodable {
            let results: [ElevationResult]
        }
        struct ElevationResult: Decodable {
             let elevation: Double
             let location: LatLngLiteral
             let resolution: Double?
            
        enum resultKeys: String, CodingKey {
            case results = "results"
            case elevation = "elevation"
            case location = "location"
            case resolution = "resolution"
            case lat = "lat"
            case lon = "lon"
            }
        }
        struct LatLngLiteral: Decodable {
             let lat: Double
             let lng: Double
        }
        guard let url = URL(string: "https://maps.googleapis.com/maps/api/elevation/json?&locations=\(finalStringConvertedCoordinates)&key=\(apiKey)") else {
                    print("Error: cannot create URL")
                    return
                }
                // Create the url request
                var request = URLRequest(url: url)
                request.httpMethod = "GET"
                let task = URLSession.shared.dataTask(with: request) { [self] data, response, error in
                    guard error == nil else {
                        print("Error: error calling GET")
                        print(error!)
                        return
                    }
                    guard let data = data else {
                        print("Error: Did not receive data")
                        return
                    }
                    //print("---> data: \(String(data: data, encoding: .utf8))")
                    guard let response = response as? HTTPURLResponse, (200 ..< 299) ~= response.statusCode else {
                        print("Error: HTTP request failed")
                        return
                    }
                    do {
                        guard let jsonObject = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                            print("Error: Cannot convert data to JSON object")
                            return
                        }
                        guard let prettyJsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: []) else {
                            print("Error: Cannot convert JSON object to Pretty JSON data")
                            return
                        }
                        guard let prettyPrintedJson = String(data: prettyJsonData, encoding: .utf8)
                        else {
                            print("Error: Could print JSON in String")
                            return
                        }
                        //print(prettyPrintedJson)
                        jsonResponse = prettyPrintedJson
                    } catch {
                        print("Error: Trying to convert JSON data to string")
                        return
                    }
                    let decoder = JSONDecoder()
                    let jrData = jsonResponse.data(using: .utf8)
                    let jsonResponseProcessed = try! decoder.decode(ElevationResponse.self, from: jrData!)

            for i in 0..<listOfPointsToQuery.count{
                        processedElevation.append(jsonResponseProcessed.results[i].elevation)
                        print(processedElevation)
                        print("processed elevation data ^")
            }
            }
        task.resume()
        completeHandler(success)
    } 

the closure is defined right after this function definition like so.

var completehandler:(Bool)->Void = { (sucess) in
        if sucess {
            print("Complete download data ")
                altitudeAdjustmentViaElevationData(elevationPointsList: processedElevation)
        } else {
            print("Error")
        }
        
    }

finally the function is called like so.

ElevationRequest(listOfPointsToQuery: finalAutoPathPoints, success: true, completeHandler: completehandler) 

a final note which seems worth mentioning. I am inexperienced so im not sure if this is of any relevance.

I am processing the json response within the same function as the call. The code is included in the first code segment above, the console response looks like this.

Complete download data 
[11.59268283843994]
processed elevation data ^
[11.59268283843994, 11.68190288543701]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553, 11.35733222961426]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553, 11.35733222961426, 11.57364559173584]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553, 11.35733222961426, 11.57364559173584, 11.7429027557373]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553, 11.35733222961426, 11.57364559173584, 11.7429027557373, 12.02133083343506]
processed elevation data ^
[11.59268283843994, 11.68190288543701, 11.86490249633789, 11.53255939483643, 11.46291351318359, 11.69645023345947, 11.79109859466553, 11.35733222961426, 11.57364559173584, 11.7429027557373, 12.02133083343506, 11.66207313537598]
processed elevation data ^ ```

Solution

  • credit given to @vidian for their help answering this question.

    Regarding the issue of execution prior to completion of the http requests response, this issue was rooted in my miss handling of the completion request.

    The tail end of the first code segment is like so.

           }
                }
            task.resume()
            completeHandler(success)
        }  
    

    it should have been structured like this.

           }
           completeHandler(success)
                }
            task.resume()
        } 
    

    another issue within the code I had written was that the closure was not defined correctly. Defining it as a function solved the issue. Correct definition of the closure is below.

     func completehandler(_ sucess : Bool) {
            if sucess {
                print("Complete download data ")
                   AltitudeAdjustmentViaElevationData(elevationPointsList: processedElevation)
            } else {
                print("Error")
            }
            
        }