Search code examples
jsonswift3completionhandler

iOS Swift 3 CompletionHandler not working


I'm trying to write a completion handler function in Swift, here's my code in Swift with a class called NBObject

typealias CompletionHandler = (_ success: Bool, _ data: [String:Any]) -> Void

// FIND OBJECTS IN BACKGROUND
func findObjectInBackground(completionHandler: CompletionHandler) {
    let tbNameStr = "table=" + theTableName
    var rStr = ""
    var theData = [String:Any]()

    for i in 0..<columns.count {
        rStr += "&c" + "\(i)" + "=" + columns[i] +
            "&r" + "\(i)" + "=" + records[i]
    }
    recordString = tbNameStr + rStr
    print("RECORD STRING: \(recordString)")

    let requestURL = URL(string: PATH_TO_API_FOLDER + "queryWhereKeyContains.php?" + recordString)

    //create the session object
    let session = URLSession.shared

    //now create the URLRequest object using the url object
    let request = URLRequest(url: requestURL!)

    //create dataTask using the session object to send data to the server
    let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in

        //exiting if there is some error
        if error != nil {
            print("Error is: \(error!.localizedDescription)")
            return
        } else {

        }

        do {
            if let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any] {
                theData = json
                print("JSON: \(json)")
            }
        } catch let error { print("ERROR IN PARSING JSON: \(error.localizedDescription)") }

    })

    task.resume()


    let flag = true
    completionHandler(flag, theData)
}

I call that function in my ViewController.swift, like this:

let query = NJObject(tableName: HOTEL_TABLE_NAME)
query.whereKeyContains(columnName: HOTEL_NAME, record: "Hotel")
query.findObjectInBackground { (succ, objects) in
    print("OBJECTS: \(objects)")
    print("SUCCESS: \(succ)")
}

So, in the Xcode console, I correctly get my JSON data, but when printing the completion handler, data (printed as OBJECTS) is empty.

OBJECTS: [:]
SUCCESS: true
JSON: ["objects": <__NSArrayM 0x17004b4c0>(
{
    address = "<null>";
    createdAt = "2017-08-12 23:08:48";
    description = "lorem ipsec dolor sit";
    email = "<null>";
},
{
    address = "<null>";
    createdAt = "2017-08-14 06:19:10";
    description = "lorem ipsec dolor sit";
    email = "<null>";
})
]

One thing I noticed is that the console first prints the OBJECTS as and empty [:] and SUCCESS logs, then the JSON data.

So I am sure there's something wrong in my findObjectInBackground() function, I just can't figure out where the issue is.


Solution

  • You have to put the completion handler into the completion handler of the data task

    You can omit the creation of the URLRequest, GET is the default, just pass the URL:

    let task = session.dataTask(with: requestURL!, completionHandler: { data, response, error in
    
        //exiting if there is some error
        if error != nil {
            print("Error is: \(error!.localizedDescription)")
            completionHandler(false, [:])
            return
        } 
    
        do {
            if let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any] {
                print("JSON: \(json)")
                completionHandler(true, json) // the variable `theData` is not needed.
            }
        } catch let error { print("ERROR IN PARSING JSON: \(error.localizedDescription)") }
          completionHandler(false, [:])
    })
    
    task.resume()
    

    And do not pass .mutableContainers, it's meaningless in Swift

    Side note: In Swift 3 this closure declaration is sufficient:

    typealias CompletionHandler = (Bool, [String:Any]) -> Void