0

I would like to set the return value equal to the value in my for loop so that I can return it from the func. Do you have an idea how to do that?

static func getStones() -> Double {
    let url = NSURL(string: "MYURL")
    let request = NSMutableURLRequest(url: url as URL!)
    var stonesNew = Double()

    let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
        let responseString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
        let contacts = responseString["Sheet1"] as? [AnyObject]

        for contact in contacts!{
            let stones = contact["stones"] as! Double
            stonesNew = stones
        }

    }
    task.resume()
    return stonesNew
}
  • So I would like to set "stonesNew" to my downloaded "stones" but it always returns stones = 0
Bista
  • 7,869
  • 3
  • 27
  • 55
Tom
  • 3
  • 3
  • Tom did u mean to set stones value to stonesNew ?? – Anuraj Apr 12 '17 at 05:32
  • you want to return total of stones? It will always return zero. – Chanchal Warde Apr 12 '17 at 05:33
  • `task.resume()` starts off the task and then returns. So when it finishes and you are returning `stonesNew`, the task has not yet completed. If you must do it this way, you need to use a semaphore to wait for the result to be returned from the server. A better approach would be to pass a handler to this function to call when the result is returned. – Gary Makin Apr 12 '17 at 05:34
  • Look for completion handlers in swift. Very helpful incase of asynchronous tasks returning values. – Bista Apr 12 '17 at 05:34

2 Answers2

1

The dataTask is an async task. So this function always returns the stonesNew immediately before the dataTask is completed. So the solution for an async task is completionHanlder, like this:

static func getStones(completion: @escaping (Double) -> Void)  {
    let url = NSURL(string: "MYURL")
    let request = NSMutableURLRequest(url: url as URL!)
    var stonesNew = Double()
    let task = URLSession.shared.dataTask(with: request as URLRequest) {data,response,error in

        let responseString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
        let contacts = responseString["Sheet1"] as? [AnyObject]

        for contact in contacts!{

            let stones = contact["stones"] as! Double
            stonesNew = stones
        }

        completion(stonesNew)
    }
    task.resume()
}

And use it like this:

MyClass.getStones(completion: { (stones) in    
    print(stones)
})
Danh Huynh
  • 2,337
  • 1
  • 15
  • 18
  • Correct me if I'm wrong but there is now way to create a reusable variable of stone for my following coding? For example if my downloaded double stones = 10.0 I would like to use this value as a normal constant for the following coding. let stonesConstant = myClass.getStones(... for example for an array: let arr = [stonesConstant] – Tom Apr 12 '17 at 10:34
0

You can't, because dataTask is async.

You have to refactor your code to accept the value of variables stonesNew at a later stage, see the following Playground:

import UIKit


let textField = UITextField()
// This is probably something like your code now... a function is doing something with the value of newStones
func buttonPressed() {
    textField.text = "\(getStones())"
}
// And this is your current getStones function 
func getStones() -> Double {
    // the code you have now
    return 0
}

// Let's see how we can do things with callbacks
func buttonPressedV2() {
    newGetStnes { (newStones) in
        textField.text = "\(newStones)"
    }
}

func newGetStnes(callback: (Double) -> ()) {
   let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
       // calculate newStones the way you were doing..
       // The following will call back your function, giving the result
       callback(newStones)
    }
}
Andre
  • 1,135
  • 9
  • 20