0

I have recently started experimenting with Swift and am new to more strongly typed programming languages.

I am trying to build a basic API call to http://openweathermap.org/api which will have a searchbox in the UIView that takes a city name and returns the relevant weather data.

My problem is figuring out how to return the JSON response I get back from my API call in my Model as a Dictionary that I can then use as a variable in my ViewController.

I have experimented with a variety of methods but continue to get a 'Dictionary not convertible to Void' error. From research and this article (Dictionary is not convertible to Void) it seems returning a closure might offer the answer but I am struggling to implement given that I only want to pass a cityname string parameter in my ViewController searchButton function.

Detailed code snippets below, thanks for help!

My API call in Model below which currently works at pulling down JSON object

class API {
func weatherSearch(#urlSearch: String) -> Dictionary<String,AnyObject>
{
    let urlPath = "http://api.openweathermap.org/data/2.5/weather?q=" + urlSearch
    let url = NSURL(string: urlPath)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
        println("Task completed")
        if(error != nil) {
            // If there is an error in the web request, print it to the console
            println(error.localizedDescription)
        }
        var err: NSError?
        if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
            var dataOut = jsonResult as Dictionary<String,AnyObject>
            return dataOut
         //omitted some additional error handling code
        }
    })
    task.resume()
 }
}

My ViewController where instantiate API and take input from Searchfield

@IBOutlet weak var searchField: UITextField!

@IBAction func searchButton() {
    let api = API()
    var dataOut = api.weatherSearch(urlSearch: searchField.text!)
    println(dataOut)
    self.performSegueWithIdentifier("Search", sender: nil)
}
Community
  • 1
  • 1
AlexHandy1
  • 457
  • 1
  • 5
  • 9
  • Your function will never work. The data is requested asynchronously but your function is not prepared for that. Add a completion block to the function like the data task does. – HorseT Jun 21 '15 at 13:57
  • @HorseT: are you referring to something along the lines of this? `if let results: NSArray = jsonResult["results"] as? NSArray { dispatch_async(dispatch_get_main_queue(), { println(results) }) }` – AlexHandy1 Jun 21 '15 at 15:06

1 Answers1

2

Using the callback technique as hinted to by the comment above, try something like this

func weatherSearch(#urlSearch: String, callback: (Dictionary<String,AnyObject> -> ())) {
    let urlPath = "http://api.openweathermap.org/data/2.5/weather?q=" + urlSearch
    let url = NSURL(string: urlPath)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
        println("Task completed")
        if(error != nil) {
            // If there is an error in the web request, print it to the console
            println(error.localizedDescription)
        }
        var err: NSError?
        if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
            var dataOut = jsonResult as! Dictionary<String,AnyObject>
            callback(dataOut)
            //omitted some additional error handling code
        }
    })
    task.resume()
}

weatherSearch(urlSearch: "endpoint here") { dictionary in
    println(dictionary)
}
Mark
  • 12,359
  • 5
  • 21
  • 37