1

I have made a mistake which I can't find. The array (class level) gets empty just before returning it.

private func loadUserData() {
    // We're getting user info data from JSON response
    let session = Twitter.sharedInstance().sessionStore.session()
    let client = TWTRAPIClient.withCurrentUser()
    let userInfoURL = "https://api.twitter.com/1.1/users/show.json"
    let params = ["user_id": session?.userID]
    var clientError : NSError?
    let request = client.urlRequest(withMethod: "GET", url: userInfoURL, parameters: params, error: &clientError)
    client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
        if connectionError != nil {
            print("Error: \(connectionError)")
        }

        do {
            let json = JSON(data: data!)
            if let userName = json["name"].string,
                let description = json["description"].string,
                let followersCount = json["followers_count"].int,
                let favouritesCount = json["favourites_count"].int,
                let followingCount = json["friends_count"].int,
                let lang = json["lang"].string,
                let nickname = json["screen_name"].string {

                self.userData.append(userName)
                self.userData.append(description)
                self.userData.append(String(followersCount))
                self.userData.append(String(favouritesCount))
                self.userData.append(String(followingCount))
                self.userData.append(lang)
                self.userData.append(nickname)
                print(self.userData)
            }

        }
    }
    print(userData)
}

I'm just trying to access to its values from other part of the code and can't do it as it gets empty. I can see the array data the first time I print it, but not in the second one. Thank you very much!

TibiaZ
  • 730
  • 1
  • 8
  • 21

2 Answers2

3

client.sendTwitterRequest is an asynchronous request that requires a network connection. It will take time to process.

You should provide a closure that returns userData and access it when the request returns.

private func loadUserData(_ callback: @escaping () -> ()) {
    // We're getting user info data from JSON response
    let session = Twitter.sharedInstance().sessionStore.session()
    let client = TWTRAPIClient.withCurrentUser()
    let userInfoURL = "https://api.twitter.com/1.1/users/show.json"
    let params = ["user_id": session?.userID]
    var clientError : NSError?
    let request = client.urlRequest(withMethod: "GET", url: userInfoURL, parameters: params, error: &clientError)
    client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
        if connectionError != nil {
            print("Error: \(connectionError)")
        }

        do {
            let json = JSON(data: data!)
            if let userName = json["name"].string,
                let description = json["description"].string,
                let followersCount = json["followers_count"].int,
                let favouritesCount = json["favourites_count"].int,
                let followingCount = json["friends_count"].int,
                let lang = json["lang"].string,
                let nickname = json["screen_name"].string {

                self.userData.append(userName)
                self.userData.append(description)
                self.userData.append(String(followersCount))
                self.userData.append(String(favouritesCount))
                self.userData.append(String(followingCount))
                self.userData.append(lang)
                self.userData.append(nickname)

                callback()     
            }
        }
    }
}

func manageUserData() {
    loadUserData() {
        print(userData)
    }
}
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
1

Explanation of OP's question by extending @the4kman answer.

Your code is in dispatch/block. The dispatch/block is called after some time.

So your first print is called immediately and at that time userdata is empty. But in second case print is called in the block and you assigns the data to the userData. So when second print is called userdata have values.

Please check below code I mentioned first and second print which is called accordingly.

For practical , put break points on both the print line and check which line is called when.

let request = client.urlRequest(withMethod: "GET", url: userInfoURL, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
    if connectionError != nil {
        print("Error: \(connectionError)")
    }

    do {
        let json = JSON(data: data!)
        if let userName = json["name"].string,
            let description = json["description"].string,
            let followersCount = json["followers_count"].int,
            let favouritesCount = json["favourites_count"].int,
            let followingCount = json["friends_count"].int,
            let lang = json["lang"].string,
            let nickname = json["screen_name"].string {

            self.userData.append(userName)
            self.userData.append(description)
            self.userData.append(String(followersCount))
            self.userData.append(String(favouritesCount))
            self.userData.append(String(followingCount))
            self.userData.append(lang)
            self.userData.append(nickname)
            print(self.userData) // 2. print second
        }

    }
}
print(userData) // 1. print first
}

Explanation of @the4kman answer

He used the callback i.e. callback is used to get data in the calling function when you received data in the called function. Here in answer he does not use any param, but you can send your array.

Note In your called func you getting data in the block. So to get data within the block like your situation you have to use completionhandler.

Please take refer:

How does a completion handler work on iOS?

Edit

in your func, make changes like this.

func loadUserData( completion: (myArray: [String]) -> Void) { // my array is array of string. Assuming you'r passing array of string from closure.

// Write the below line where you wanted to call callback or return through closure. 
// Recommandated to use below line in second print.

    completion(userData) // 'userData' is the array which you wanted to pass from closure.

}

How to call closure.

loadUserData {
    (result: [String]) in
    print("got back: \(result)")
}

Note : May be you need to change your closure to @escaping, for this x-code gives you suggestion. So first try my edit answer.

dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
  • As you say, I've checked it and the prints are called in the order you mention. How could I solve this then ? – TibiaZ Jul 25 '17 at 13:19
  • @JavierSanzRozalén Please check my updated answer i already mentioned. – dahiya_boy Jul 25 '17 at 13:20
  • But what if I want to return that array from anywhere ? I've got stuck as I have never used closures and I am a newbie at Swift. Sorry for keeping asking. – TibiaZ Jul 25 '17 at 15:31
  • @JavierSanzRozalén Thats ok buddy we happy to help you. Ask where you got stuck. Closures are simple to use. – dahiya_boy Jul 26 '17 at 06:57
  • @JavierSanzRozalén check my edit asnwer in the bottom of my answer. – dahiya_boy Jul 26 '17 at 07:09
  • You've helped me so much! Thank you mate, finally undestood the concept of closures in Swift! – TibiaZ Jul 26 '17 at 09:39