1
var serviceTypeName = [String]()

override func viewDidLoad() {
    super.viewDidLoad()
    getServiceType()
    debugPrint(serviceTypeName)
}

I try to get an array of names from web API, and store it into the array serviceTypeName. Below is my function of get service name (I used Alamorfire lib)

func getServiceType() {
            Alamofire.request(...
                .responseJSON { response in
                    switch response.result {
                    case .success:

                        if let responseValue = response.result.value {
                            var serviceName: [String] = []
                            let json = JSON(responseValue)

                            for (key, subJson):(String, JSON) in json["results"] {

                                if let name = subJson["name"].string {
                                    serviceName.insert(name, at: Int(key)!)

                                }
                            }
                            self.serviceTypeName = serviceName

                        } ...

However, I got null array [] after this function, just like the function didn't do anything. I make sure that I got correct data from server and put into serviceName correctly every loop. But I cannot set it to the serviceTypeName. Would you please point out the mistakes?

Dan
  • 301
  • 3
  • 21

2 Answers2

2

I got null array [] after this function, just like the function didn't do anything.

That's right, the function did not do anything. More precisely, the function did not do anything yet. Checking the value after the function is premature, because the callback is executed asynchronously, when the data arrives.

You need to split your code into two parts:

  • The first part initiates the call, and gives it a callback that sets properties to the results received from the call
  • The second part processes the results, displaying the properties or doing whatever else you planned to do

Once the callback is done setting the properties, it needs to initiate the call for the second part, which makes use of these properties.

func getServiceType() { // Part 1
    ...
    self.serviceTypeName = serviceName
    dispatch_async(dispatch_get_main_queue()) {
        processServiceName()
    }

}

func processServiceName() { // Part 2
    ... // Do things with self.serviceTypeName
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • `DispatchQueue.main.async { ... }` I used this instead of `dispatch_async...` which will lead to error – Dan Sep 27 '16 at 15:42
  • @user2174595 I see, you are on Swift-3, right? What does your `processServiceName()` function does, and what kind of error do you get? – Sergey Kalinichenko Sep 27 '16 at 15:44
  • Yes. It might be just the syntax difference in swift 3.0. I reload UITableView in the func. – Dan Sep 27 '16 at 15:50
  • @user2174595 Does it crash, or does the data not get reloaded? An answer to [this question](http://stackoverflow.com/q/24112844/335858) suggests that your code should work. – Sergey Kalinichenko Sep 27 '16 at 15:54
  • Thanks! It works. @Edgar 's answer is also great too by using `completionHandler` in Alamofire – Dan Sep 28 '16 at 01:02
2

Alamofire requests are asynchronous. That means that you code in getServiceType() will be running separately from the rest of the code in your viewDidLoad() function.

At the time debugPrint(serviceTypeName) is called the getServiceType() function has not finished processing.

A way to fix that is to add a completionHandler to your getServiceType() function :

func getServiceType(completionHandler: () -> ()) {
            Alamofire.request(...
                .responseJSON { response in
                    switch response.result {
                    case .success:

                        if let responseValue = response.result.value {
                            var serviceName: [String] = []
                            let json = JSON(responseValue)

                            for (key, subJson):(String, JSON) in json["results"] {

                                if let name = subJson["name"].string {
                                    serviceName.insert(name, at: Int(key)!)

                                }
                            }
                            self.serviceTypeName = serviceName
                            completionHandler()
                        } ...

Then you can call the func like that :

override func viewDidLoad() {
    super.viewDidLoad()
    getServiceType() {
        debugPrint(serviceTypeName)
    }
}

here the debug print will be called when the completion handler is triggered by your asynchronous function : at the right time. :)

Edgar
  • 203
  • 2
  • 9
  • Here is what the Xcode told me to add `func getServiceType(completionHandler: @escaping () -> ()) {` . What's the `escaping` mean here? – Dan Sep 27 '16 at 15:40
  • No problem ! I wouldn't feel confident in explaining you what closure / type attributes are, so i'l just leave that here for more info : [updating-closures-to-swift-3-escaping](http://stackoverflow.com/questions/39063499/updating-closures-to-swift-3-escaping) – Edgar Sep 28 '16 at 08:32