0

I'm parsing JSON data from a remote service. i wrote a function wich do the parsing process. This function has a return value. The result is created in this function and saved in a global property. But when i call the function in viewDidLoad i get an empty result: Here is my code

class ViewController: UIViewController {

    var rates = [String:AnyObject]()

    override func viewDidLoad() {
        super.viewDidLoad()

        print(getRates("USD")) // <- Gives me an empty Dictionary
    }

    func getRates(base: String) -> [String:AnyObject]{

        let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
        let urlSession = NSURLSession.sharedSession()

        let task = urlSession.dataTaskWithURL(url) { (data, response, error) in

            do{
                self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]

                //print(self.rates) //<-- Gives me the right output, but i want to use it outside.

            }
            catch{
                print("Something went wrong")
            }

        }

        task.resume()
        return self.rates //<- returns an empty Dictionary
    }

I can only get the right result inside the function, but I can't use it outside. What is wrong here?

EDIT: Tank you! All answers are working, but is there a way to store the result in a global property so that i can use the result anywhere? Assuming i have a tableView. Then i need to have the result in a global property

user1895268
  • 1,559
  • 3
  • 11
  • 23

3 Answers3

1

Because you are using NSURLSession and the task is asynchronous you will need to use a completion handler. Here is an example:

//.. UIViewController Code

var rates = [String: AnyObject]()

override func viewDidLoad() {
    super.viewDidLoad()
    getRates("USD") { [weak self] result in
        self?.rates = result
    }
}


func getRates(base: String, completion: [String: AnyObject] -> Void) {

    let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
    let urlSession = NSURLSession.sharedSession()
    let task = urlSession.dataTaskWithURL(url) { (data, response, error) in

        do {
            let rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
            completion(rates)
        }
        catch {
            print("Something went wrong")
        }
    }
    task.resume()
}
damianesteban
  • 1,603
  • 1
  • 19
  • 36
  • Thank you. But is there a way to store the result in a global property? – user1895268 Aug 12 '16 at 18:11
  • That should do the trick :) However I would recommend that you do not write methods like this directly in the viewcontroller. Instead try to make a struct or class, let's call it `NetworkService` that contains your networking logic. When your app is small it isn't a big deal, but as it grows your viewcontroller will become massive and unruly. – damianesteban Aug 12 '16 at 18:29
1

You cannot return response value at once - you have to wait until response arrives from network. So you have to add a callback function (a block or lambda) to execute once response arrived.

class ViewController: UIViewController {

    var rates = [String:AnyObject]()

    override func viewDidLoad() {
        super.viewDidLoad()

        getRates("USD"){(result) in
            print(result)
        }
    }

    func getRates(base: String, callback:(result:[String:AnyObject])->()){

        let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
        let urlSession = NSURLSession.sharedSession()

        let task = urlSession.dataTaskWithURL(url) { (data, response, error) in

            do{
                self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
                callback(self.rates)
                //print(self.rates) //<-- Gives me the right output, but i want to use it outside.

            }
            catch{
                print("Something went wrong")
            }

        }

        task.resume()
    }
fnc12
  • 2,241
  • 1
  • 21
  • 27
0

Try this on your code:

class ViewController: UIViewController {

    var rates = [String:AnyObject]()

    override func viewDidLoad() {
        super.viewDidLoad()

        getRates() { (result) in
            print(result) 

        }
    }

    func getRates(completion: (result: Array)) -> Void{

        let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
        let urlSession = NSURLSession.sharedSession()

        let task = urlSession.dataTaskWithURL(url) { (data, response, error) in

            do{
                self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
                completion(self.rates)
            }
            catch{
                print("Something went wrong")
            }

        }

        task.resume()
        return self.rates //<- returns an empty Dictionary
    }
}
Mário Carvalho
  • 3,106
  • 4
  • 22
  • 26