Search code examples
swiftasynchronousalamofire

Need help understanding how to handle completion handlers with multi level function calls


I am looking for some help dealing with my code. I am using Alamofire to go GETs and POSTs. Since they are run asynchronously, I need to use completion handlers so that I can save the JSON data to a variable and use it later.

My code is below, but I think explaining might make things clearer. Function x() needs the JSON data and first calls function y(). Function y() then calls the getRequest function to actually do the GET. The getRequest completion handler is, I think, set up correctly. It is set up so that y() can have it's own completion handler code. I am now stuck on how to get the JSON data out to the original function x().

Do I have to do another completion: @escaping (JSON?) -> Void) and completion(response) in function y() and then add completion code in all functions that call y or is there a way that I can export the JSON data so that I can use it in function x()?

func getRequest(url: String, params: [String:[String]] = ["":[""]], completion: @escaping (JSON?) -> Void) {

    let headers: HTTPHeaders = [ /* Header data goes here */ ]
    AF.request(url, method: .get, parameters: params, headers: headers).validate().responseJSON { (response) in
        // completion code goes here
        completion(response)
    }
    
}

func y() -> JSON {
    
    var jsonData = JSON(0)
    
    let url = "https://url.com/v1/"
    getRequest(url: url) { value in
        // Add completion code here too?
    }
    
    // Since get_request uses AF.request, the reply comes asynchronously and we don't return the actual data
    return jsonData

}

func x(token: String) -> JSON {
    
    let json = y()
    let jsonData = json["A"]

    // Rest of code goes here
}

Solution

  • Once a function uses an asynchronous function, it also becomes asynchronous because it cannot synchronously return the data, so it too needs to have its own completion handler.

    So y should be defined as something like this:

    func y(completion: @escaping (JSON) -> Void) {
       // ...
       getRequest(url: url) { value in
          print(value) // or do something else with value
          completion(value) // return data via callback
       }
    }
    

    Same with x - it needs some completion to return data or to signal that it's done:

    func x(token: String, completion: @escaping () -> Void) {
    
       // ...
       y() { json in
          print(json)
          completion() // or return something via callback
       }
    }