0

I am new with iOS programming. I am trying to make a piece of code in my function be synchronized, but it doesn't seem to work:

func fetchLocationsList(searchText:String)->Array<String> {


        print ("searched text:\(searchText)")
        let url = URL(string:"http://api.openweathermap.org/data/2.5/find?q=\(searchText)&type=like&sort=name&cnt=9&APPID=a33aa72")

        //Using Alamofire to handle http requests
        Alamofire.request(url!).responseJSON {response in
            guard let jsonResponse = response.result.value as? [String:Any]
                else { print ("error in json response")
                    return}

            guard let list = jsonResponse["list"] as? NSArray else {return}
            let lockQueue = DispatchQueue(label:"Et.My-Weather-App.queue1")
            _ = lockQueue.sync{
                for index in 0..<list.count {
                    print ("index is: \(index)")
                    guard let listElement = list[index] as? [String:Any] else {return}
                    let id = listElement["id"] as! Int
                    print ("id is: \(id)")
                    let cityName = listElement["name"] as! String
                    print ("cityName is: \(cityName)")
                    let sys = listElement["sys"] as! [String:Any]
                    let country = sys["country"] as! String
                    print ("country is: \(country)")
                    let element = "\(cityName), \(country), \(id)"
                    print ("\(element)")
                    self.resultsArray.append(element)
                }
            }
        }
        if self.resultsArray.count==0 {
            print ("results array is also zero!")
        }

        return self.resultsArray
    }

When I run it, I see that the line "results array is also zero!" is printed before the "for" loop fills the resultArray with elements, so the returned resultArray is always empty! What am I doing wrong?

benh
  • 25
  • 8
  • You can't `return` a value from an asynchronous process, such as retrieving something from the network. Rather, pass a closure to your `fetchLocationsList` function and invoke that from the network completion closure. Understanding Asynchronous processes and coding for them is very important in iOS; do not simply try and make the calls synchronous. It will make your app non-responsive. – Paulw11 May 07 '17 at 23:08
  • @Paulw11, my question may look duplicated, but I wouldn't think of looking for a question like "how to return value from Alamofire". My question was about synchronized code, it wasn't about Alamofire. – benh May 07 '17 at 23:52
  • Ok, but the answer you have accepted pointlessly dispatches the `for` loop onto another queue; the real fix is that the `completion(array)` is now *inside* the AlamoFire closure, which is the solution I mentioned and the solution in the duplicate. `fetchLocationList` is *still* not a synchronous function; note that it has no return statement. If you did want to block `fetchLocationList` until the network operation completed, the solution would be to use a `DispatchGroup`, but synchronous functions are a **bad idea** – Paulw11 May 07 '17 at 23:57
  • And don't feel bad about asking a duplicate question; while it is nice if you can search and find what you are looking for, in this case you didn't. Now your question is added to the collection and if someone else searches they may find *your* question; The duplicate link helps *them* find the right answer, – Paulw11 May 08 '17 at 00:03
  • You're right, I removed the other queue in my code. I'm a beginner, nothing but bad ideas. :) – benh May 08 '17 at 00:08
  • Thanks for clarifying that. I'm a beginner in stackoverflow as well.. – benh May 08 '17 at 00:09
  • Don't knock yourself; Nothing worthwhile is easy. There a lot of concepts in mobile development, such as asynchronous programming, that you need to get a handle on; you can't expect instant success. :) – Paulw11 May 08 '17 at 00:10

1 Answers1

-1

I suggest you do this as async tasks are a pain and this works quite well.

func fetchLocationsList(searchText:String, completion: @escaping (_ results:Array<String>)->()){

    print ("searched text:\(searchText)")
    let url = URL(string:"http://api.openweathermap.org/data/2.5/find?q=\(searchText)&type=like&sort=name&cnt=9&APPID=a33aa72")

    //Using Alamofire to handle http requests
    Alamofire.request(url!).responseJSON {response in

        guard let jsonResponse = response.result.value as? [String:Any] else { print ("error in json response"); return}

        guard let list = jsonResponse["list"] as? Array<Dictionary<String,Any>> else { return }

        var array = Array<String>() // create an array to store results.

        for item in list {

            let id = item["id"] as! Int

            let cityName = item["name"] as! String

            let sys = item["sys"] as! Dictionary<String,Any>

            let country = sys["country"] as! String

            let element = "\(cityName), \(country), \(id)"

            array.append(element) // add to that array.
        }

        completion(array) //send the array via the completions handler.
    }
}

So in your viewDidLoad or whatever.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    fetchLocationsList(searchText: "Whatever this string is") { (results) in

        self.resultsArray.append(contentsOf: results)

        // Then do anything else you need to do after this function has completed within this closure.
    }
}
Tristan Beaton
  • 1,742
  • 2
  • 14
  • 25
  • That's perfect. Tx! – benh May 07 '17 at 23:40
  • 2
    The `lockQueue.sync` is completely unnecessary. – Paulw11 May 07 '17 at 23:58
  • 1
    Look @Paulw11 I was just added the code to solve his problem. It was not part of the original question to check if what his other code was correct. So your previous comment is completely unnecessary. Also if you were the one who downvoted my answer, you should reconsider as it answers the question, and the fact that other parts of his original code may be incorrect, should not be reflected on my answer. – Tristan Beaton May 08 '17 at 02:14
  • @benny That's good. Glad I could help. – Tristan Beaton May 08 '17 at 02:51
  • You are not calling the completion handler in case of an error. Bad things will happen :) – CouchDeveloper May 13 '17 at 18:10
  • @CouchDeveloper I am aware of that. It is up to the person who asks the question to determine how he wants to handle the errors. I have print statements for any errors, and it is up to that person to decide if the completion handler is called. – Tristan Beaton May 13 '17 at 20:52