4

I'm new to Swift and I'm trying to get the result from this function. I don't know how to access variables that are inside the closure that is passed to the sendAsynchronousRequest function from outside the closure. I have read the chapter on closures in the Apple Swift guide and I didn't find an answer and I didn't find one on StackOverflow that helped. I can't assign the value of the 'json' variable to the 'dict' variable and have that stick outside of the closure.

    var dict: NSDictionary!
    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response, data, error) in
        var jsonError: NSError?
        let json = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? NSDictionary
        dict = json
        print(dict) // prints the data
    })
    print(dict) // prints nil
Questioner
  • 2,451
  • 4
  • 29
  • 50
  • 2
    The whole point of a closure is that the statement inside the closure completes asynchronously. That means the closure is not finished when you tried to print dict outside of it. Which means what you want to do with dict should be done in the closure. – Swift Rabbit Aug 07 '15 at 13:32
  • Exactly, @SwiftRabbit is right. – Deny Aug 07 '15 at 14:04

2 Answers2

4
var dict: NSDictionary! // Declared in the main thread

The closure is then completed asynchronously so the main thread doesn't wait for it, so

println(dict)

is called before the closure has actually finished. If you want to complete another function using dict then you will need to call that function from within the closure, you can move it into the main thread if you like though, you would do this if you are going to be affecting UI.

var dict: NSDictionary!
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response, data, error) in
    var jsonError: NSError?
    let json = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? NSDictionary
    dict = json
    //dispatch_async(dispatch_get_main_queue()) { //uncomment for main thread
        self.myFunction(dict!)
    //} //uncomment for main thread
})

func myFunction(dictionary: NSDictionary) {
    println(dictionary)
}
Swinny89
  • 7,273
  • 3
  • 32
  • 52
  • Thanks but I get 'Cannot invoke 'myFunction' with an argument list of type '(NSDictionary)' when I try and use the main thread. – Questioner Aug 07 '15 at 13:52
  • Have amended the code to fix that error, dict needs unwrapping before passing it into the function using a ! – Swinny89 Aug 07 '15 at 13:53
  • edit: getting the error Cannot invoke 'dispatch_async' with an argument list of type '(dispatch_queue_t!, () -> Void)' – Questioner Aug 07 '15 at 13:59
  • Please send me your code, I need to see what kind of context it is in, whether its all in one function etc – Swinny89 Aug 07 '15 at 14:01
  • I figured out the source of the problem a few days ago. It was really quite a simple mistake. I had made a static function and forgot I had made it static and that was what was causing all the problems. – Questioner Aug 11 '15 at 10:51
2

You are calling an asynchronous function and printing act without waiting for it to finish. In other words, when print(dict) is called, the function hasn't complete execution(hencedict is nil)

Try something like

var dict: NSDictionary!
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response, data, error) in
    var jsonError: NSError?
    let json = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? NSDictionary
    dict = json
    doSomethingWithJSON(dict)
})

and put your JSON logic inside a doSomethingWithJSON function:

void doSomethingWithJSON(dict: NSDictionary) {
    // Logic here
}

This ensures that your logic is execute only after the URL request completes.

Roshan
  • 1,937
  • 1
  • 13
  • 25