Can Joe
Can Joe

Reputation: 13

JSON Completion Handler

This is the code I am currently using:

func fetchOne(){
        URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
            guard let data = data, error == nil else { return }
            do {
                let allContactsData = try Data(contentsOf: self.apiURL!)
                let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
                if let arrJSON = allContacts["data"] as? [[String : Any]] {
                    for aObject in arrJSON {
                        self.followerUsername.append(aObject["username"] as! String)
                        self.followerFullName.append(aObject["full_name"] as! String)
                    }
                }
//                print(self.followerUsername)
//                print(self.followerFullName)

            } catch let error as NSError {
                print(error)
            }
        }).resume()
    }

How can I detect when the json has finished fetching all the info in data and then run a new function fetchTwo() ?

Upvotes: 0

Views: 1389

Answers (2)

Rashwan L
Rashwan L

Reputation: 38833

If your method is asynchronous then you can add a completionHandler to you function, like this:

func fetchOne(onCompletion: @escaping (Bool) -> Void, onError: @escaping (NSError) -> Void) {
    URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            let allContactsData = try Data(contentsOf: self.apiURL!)
            let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
            if let arrJSON = allContacts["data"] as? [[String : Any]] {
                for aObject in arrJSON {
                    self.followerUsername.append(aObject["username"] as! String)
                    self.followerFullName.append(aObject["full_name"] as! String)
                }
                onCompletion(true)
            }
//                print(self.followerUsername)
//                print(self.followerFullName)

        } catch let error as NSError {
            print(error)
            onError(error)
        }
    }).resume()
}

Usage:

fetchOne(onCompletion: { (successful) in
    print(successful)
    fetchTwo()
}) { (error) in
    print(error.domain)
}

If it´s synchronous then just do this:

func fetchOne(){
    URLSession.shared.dataTask(with:apiURL!, completionHandler: {(data, response, error) in
        guard let data = data, error == nil else { return }
        do {
            let allContactsData = try Data(contentsOf: self.apiURL!)
            let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
            if let arrJSON = allContacts["data"] as? [[String : Any]] {
                for aObject in arrJSON {
                    self.followerUsername.append(aObject["username"] as! String)
                    self.followerFullName.append(aObject["full_name"] as! String)
                }
            }
//                print(self.followerUsername)
//                print(self.followerFullName)
                  fetchTwo()

        } catch let error as NSError {
            print(error)
        }
    }).resume()
}

Update:

fetchOne(onCompletion: { (successful) in
    print(successful) // fetch one
    fetchTwo(onCompletion: { (successful) in
        print(successful) // fetch two
    }) { (error) in
        print(error.domain)
    }
}) { (error) in
    print(error.domain)
}

Upvotes: 1

Wang90925
Wang90925

Reputation: 142

Welcome to Swift :)

You are mixing synchronous and asynchronous code together.

When you call login you expect it to return an answer straight away of type [String : String].

But in your login method you then do a network call which can not return straight away...which is why the call to Alamofire.requesttakes a completion block as a parameter.

So...you need to change your login method so it:

does not return anything straight away (it can not do so...logging in requires us doing a network call remember) takes a completion block to invoke once login has succeeded. This can be done like so:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ())

Welcome to Swift :)

You are mixing synchronous and asynchronous code together.

When you call login you expect it to return an answer straight away of type [String : String].

But in your login method you then do a network call which can not return straight away...which is why the call to Alamofire.requesttakes a completion block as a parameter.

So...you need to change your login method so it:

does not return anything straight away (it can not do so...logging in requires us doing a network call remember) takes a completion block to invoke once login has succeeded. This can be done like so:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ()) Here we have a function that takes a userName of type String, a password of type String and a loginCompletion of type function that again takes a [String : String] dictionary as a parameter. Notice that the method does not return anything.

Now you can call your makeWebServiceCall almost as before:

let loginrequest = JsonRequests.loginRequest(userName: userName, password: password)
makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in
   //Now we are ready, the login call has returned some data to you. 

   //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
   loginCompletion(yourConvertedDictionaryHere)
})

Here is the new login method in its completeness:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ()) {
    let loginrequest = JsonRequests.loginRequest(userName: userName, password: password)
    makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in
       //Now we are ready, the login call has returned some data to you. 

       //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
       loginCompletion(yourConvertedDictionaryHere)
    })
}

Welcome to Swift :)

You are mixing synchronous and asynchronous code together.

When you call login you expect it to return an answer straight away of type [String : String].

But in your login method you then do a network call which can not return straight away...which is why the call to Alamofire.requesttakes a completion block as a parameter.

So...you need to change your login method so it:

does not return anything straight away (it can not do so...logging in requires us doing a network call remember) takes a completion block to invoke once login has succeeded. This can be done like so:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ()) Here we have a function that takes a userName of type String, a password of type String and a loginCompletion of type function that again takes a [String : String] dictionary as a parameter. Notice that the method does not return anything.

Now you can call your makeWebServiceCall almost as before:

let loginrequest = JsonRequests.loginRequest(userName: userName, password: password) makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in //Now we are ready, the login call has returned some data to you.

//You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so: loginCompletion(yourConvertedDictionaryHere) }) Here is the new login method in its completeness:

public func login(userName: String, password: String, loginCompletion: @escaping ([String : String]) -> ()) { let loginrequest = JsonRequests.loginRequest(userName: userName, password: password) makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in //Now we are ready, the login call has returned some data to you.

   //You have an attribute named JSON of type Any, which you need to convert to [String : String], and then you can call loginCompletion with that, like so:
   loginCompletion(yourConvertedDictionaryHere)
})

} And then you call your login method like so:

retur.login(userName: "root", password: "admin01") { stringDictionary: [String : String] in
    //here you have your stringDictionary which you can use as pleased
}

Hope that helps you.

Upvotes: 0

Related Questions