0

So I have a closure called task which is a post request and I have a global variable called values and I'm trying to set the value of "values" to be the data that I retrieved back from database stored in a variable called "array". Don't worry about the tableview.reloadData part, that's already done. i just wanna know how to get the value out of closure.

var values:NSArray = []

@IBOutlet weak var Open: UIBarButtonItem!
override func viewDidLoad() {
    super.viewDidLoad()


    Open.target = self.revealViewController()
    Open.action = #selector(SWRevealViewController.revealToggle(_:))
    self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
    get()
    print ("values=\(values)")

}
func get(){       
    let request = NSMutableURLRequest(URL: NSURL(string: "http://www.percyteng.com/orbit/getAllpostsTest.php")!)
    request.HTTPMethod = "POST"
    let postString = "user=\("ios")"
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in
        if error != nil {

            print("error=\(error)")
            return
        }

        print("response = \(response)")

        let array = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray

        dispatch_async(dispatch_get_main_queue()) { [unowned self] in
            self.values = array
            print ("error=\(self.values)")
            self.tableView?.reloadData();
        }
    }
    task.resume()
Percy Teng
  • 53
  • 2
  • 9
  • I don't get it. What do you want to do ? Take "data" out of your closure ? To do what ? – Randy Jun 30 '16 at 03:06
  • so i have a global variable var values:NSArray = [] and I retrieved data from database here let array = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray ; i want to set the value of that global variable to be the value of variable "array" – Percy Teng Jun 30 '16 at 03:09
  • You're already doing that with the `self.values = array` line. What is your issue? – dan Jun 30 '16 at 03:16
  • so the "values" in that closure has the correct data i want. but outside the closure, let's say. if I do print(values) below task.resume(). It's going to print nothing. So I want the data to be passed to the global variable "values" from the closure – Percy Teng Jun 30 '16 at 03:19
  • 1
    It prints nothing because you are printing it before the network call finishes. Networking takes time and your app doesn't want to just freeze during it so it does it in the background while it continues executing. – dan Jun 30 '16 at 03:20
  • I get that, I want a solution how to pass the data out of the closure – Percy Teng Jun 30 '16 at 03:29
  • 1
    You can't pass data out of the closure, at least not in a meaningful way. You need to pass a completionHandler to the function as per the answer below. In that completionHandler closure you will perform whatever tasks you need to with the value that has been retrieved. – Paulw11 Jun 30 '16 at 03:48

1 Answers1

3

use completion for any asynchronous task in closure

func get(completion:(value: NSArray) -> Void){

    // request part
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in
        // your serialization code

        dispatch_async(dispatch_get_main_queue()) { [unowned self] in
            self.values = array

            // return value to completion
            completion(value: array)

            print ("error=\(self.values)")
            self.tableView?.reloadData();
        }
    }
} 

change the way you get the value in viewdidload

   get{(value) in
       // finish NSURLSession task and everything should be done in this closure
        self.values = value
        print ("values=\(self.values)")
    }
xmhafiz
  • 3,482
  • 1
  • 18
  • 26
  • I tried this, and it gives me an error "Use of unresolved identifier "completion"" on the line completion(value:array) – Percy Teng Jun 30 '16 at 03:22
  • have you add to your function parameter? – xmhafiz Jun 30 '16 at 03:23
  • updated my code for func paramater. now should be ok – xmhafiz Jun 30 '16 at 03:36
  • There is no error anymore but now how do I use the data passed to completion to assign values to my global variable "values"? – Percy Teng Jun 30 '16 at 03:42
  • so I did get{(value) in self.values = value } but it still doesn't change the value of "values" to what I want – Percy Teng Jun 30 '16 at 03:49
  • where did you print the self.values? it should be after finish that completion – xmhafiz Jun 30 '16 at 03:52
  • hmm can u see the change? stack overflow says i need to do peer review for u to see it. anyway i just did get{(value) in self.values = value } print ("values=\(self.values)") – Percy Teng Jun 30 '16 at 03:56
  • yes, I edit a bit to make sure everything called within that get() completion handler – xmhafiz Jun 30 '16 at 03:58
  • but it still doesn't do what I want. I want to save the value to that global variable out of the whole closure and completion thing so i can implement those values later with other stuff – Percy Teng Jun 30 '16 at 03:59
  • for example, i have table view and i need to use data when i go into a subview after i select one of the table cell – Percy Teng Jun 30 '16 at 04:00
  • do you mean use the value to another controller? or another method? If in the same viewController should be no problem. Justt take note, any UI or view changes should be after the task done. – xmhafiz Jun 30 '16 at 04:12
  • what should i do if I want to pass those data as parameters to another view controller? that could be an issue cuz they never get saved in the main thread of the current view controller right? – Percy Teng Jun 30 '16 at 04:21
  • it's normal issue when dealing with API data. need to wait it finishing task, then update the view which enable user to interact. for example, disable button until the request task done. – xmhafiz Jun 30 '16 at 04:26
  • U said any UI or view change should be done after the task is finished. Does that mean any UI or view change I wanna make, I make them in the completion handler? But still, how do I pass data to another view controller tho? – Percy Teng Jun 30 '16 at 04:30
  • yes after finish. And to pass data you can use segue or maybe global variable (accessible by all controllers and class) using http://stackoverflow.com/questions/26195262/how-to-create-a-global-variable – xmhafiz Jun 30 '16 at 04:36
  • do I pass data to another view controller from completion handler too? Or I need to do it outside that task? If I do it outside the task, I don't even have the data saved anywhere, how can I pass to another view controller? – Percy Teng Jun 30 '16 at 04:48
  • the steps will be like this. get the data -> reload table in completion -> pass the data when didSelectRowAtIndexPath. it should be ok because you already have the value – xmhafiz Jun 30 '16 at 04:53
  • that works but i am saying if I want to pass parameters through prepareForSegue(), how would I do that? Thanks tho, I'm almost done my questions! – Percy Teng Jun 30 '16 at 12:14
  • You can prepareForSegue from selected row. Refer here for detail http://stackoverflow.com/questions/29974864/how-to-access-segue-in-didselectrowatindexpath-swift-ios – xmhafiz Jun 30 '16 at 12:37
  • THANK YOU SO MUCH! – Percy Teng Jun 30 '16 at 13:07