Search code examples
swiftnsurlsessionnsurlrequest

Wait response before proceeding


I'm working to an iOS Swift App and I am trying to get the content of a website. The problem is that, when I run the code, the response object is returned after other lines are executed.

I have this:

public var response: Array<Any>?

public func myRequest(completion: @escaping (_ json: Any?, _ error: Error?)->()) {
    var request = URLRequest(url: self.url)
    request.httpBody = postString.data(using: .utf8)
    let t = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data,
        error == nil else {
            completion(nil, error)
            return
        }
        let json = try? JSONSerialization.jsonObject(with: data, options: [])
        if let dictionary = json as? [String : Any] {
            if (dictionary["ok"] as? String == "true") {
                self.response = dictionary["users"] as? Array<Any>
            }
        }
        completion(json, error)
    }
    t.resume()
}

And then:

func foo() {
    myRequest() { json, error in
        print(json)
    }
    print("I'm here!")
}

And I'm getting this:

I'm here
{...} //JSON

The question is: why I'm retrieving I'm here before JSON? How can I solve it?


Solution

  • Here's an example (based on your code) of how to have myRequest accept a completion block and to call it once the JSON has been deserialized (or not).

    public func myRequest(completion: @escaping (_ json: Any?, _ error: Error?)->())
    {
        var request = URLRequest(url: self.url)
        request.httpBody = postString.data(using: .utf8)
        let t = URLSession.shared.dataTask(with: request) 
        { data, response, error in
            guard let data = data,
                      error == nil else
            {
                completion(nil, error)
                return
            }
            let json = try? JSONSerialization.jsonObject(with: data, options: [])
            //Do other things
            completion(json, error)
        }
        t.resume()
    }
    

    And here's how you would call it:

    func foo()
    {
        myRequest()
            { json, error in
                // will be called at either completion or at an error.
            }
    }
    

    Now if you're NOT on main thread, and truly want to wait for your myRequest() to complete, here's how (there are many ways to do this, btw):

    func foo()
    {
        let group = DispatchGroup()
        group.enter()
    
        myRequest()
            { json, error in
                // will be called at either completion or at an error.
                group.leave()
            }
        group.wait() // blocks current queue so beware!
    }