4

The current code I'm having doens't seem to return anything, I can't find out what is causing the issue.

func getQuests(category: NSString, count: Int) -> NSArray {
    var quests = NSArray()

    Alamofire.request(.GET, apiUrlString, parameters: ["category": category, "count": count])
        .responseJSON { (request, response, json, error) in
            dispatch_async(dispatch_get_main_queue(), {
                quests = json as NSArray
            })
    }

    println(quests)  #=> ()

    return quests
}

Does anybody know how to solve the issue I'm having?

[Update]: This is the status.

Please look at the fifth and eight row. I can't get the assignment to quests work.

var quests = NSArray()

getQuests("normal", count: 30, completionHandler: {
    quests in
        self.quests = quests
    })

println(self.quests)  #=> ()

func getQuests(category: NSString, count: Int, completionHandler: (NSArray -> Void)) {
    var quests = NSArray()

    Alamofire.request(.GET, apiUrlString, parameters: ["category": category, "count": count])
        .responseJSON { (request, response, json, error) in
            dispatch_async(dispatch_get_main_queue(), {
                quests = json as NSArray
                completionHandler(quests)
            })
    }
}

Thanks.

Thomas Krajacic
  • 2,488
  • 1
  • 21
  • 25

3 Answers3

6

The other answers are certainly correct and hit on many of the issues you are running into with asynchronous operations. I'd merely like to add the fact that the dispatch_async(dispatch_get_main_queue()) call is not necessary.

This is already being done automatically inside of Alamofire. Alamofire handles all operations on an internal delegate dispatch queue. Once all those operations are completed (validation, response serialization, etc.), your completion handler closure is called on the main dispatch queue by default. This makes the dispatch_async unnecessary and it should be removed.

You can also run your completion handlers on your own provided dispatch queue if you like, but that's certainly an advanced feature that is not applicable to this use case.

Here's a more concise version of the same logic.

let apiUrlString = "some/url/path"

func getQuests(#category: NSString, count: Int, completionHandler: (NSArray) -> Void) {
    Alamofire.request(.GET, apiUrlString, parameters: ["category": category, "count": count])
             .responseJSON { _, _, json, _ in
                 completionHandler(json as NSArray)
             }
}

var myQuests: NSArray?

getQuests(category: "normal", count: 30) { quests in
    myQuests = quests
    println("My Quests: \(myQuests)")
}
cnoon
  • 16,575
  • 7
  • 58
  • 66
  • Hi @Kaito, if this answer ended up helping you solve your problem, could you mark it as such? Your question has certainly been answered multiple times here, and it would be awesome if you could give someone the credit to be a good community user. Cheers. – cnoon Mar 08 '15 at 17:27
3

When doing asynchronous work inside a function, it is not possible to return the value as you would like to. Functions that have asynchronous parts in it usually let you pass in a "completion handler", which will get executed once the asynchronous task is complete.

In your case, this would mean, you have to change your function "getQuests" like this for example:

func getQuests(category: NSString, count: Int, completionHandler: (NSArray -> Void)) {

    Alamofire.request(.GET, apiUrlString, parameters: ["category": category, "count": count])
        .responseJSON { (request, response, json, error) in
            dispatch_async(dispatch_get_main_queue(), {
                let quests = json as? NSArray
                // pass the array of quests, or an empty array to your completionHandler
                completionHandler(quests ?? [])
            })
    }
}

You can then call this function from somewhere and pass in the completion handler where you do something with the quests retrieved:

getQuests("Easy", count: 5, completionHandler: {
    quests in
        println(quests)
    })

Hope this gets you started.

Thomas Krajacic
  • 2,488
  • 1
  • 21
  • 25
  • Thank you for answer. But I couldn't. – Kaito Miyazaki Feb 18 '15 at 07:34
  • I am afraid it seems you really need to learn more basic programming concepts before starting with asynchronous stuff. In your case, you would need to call 'println()' inside the completion block for it to work. – Thomas Krajacic Feb 18 '15 at 08:13
  • And what does self refer to? Is the code inside a class or on a global namespace? What is the actual error message that Xcode tells you? – Thomas Krajacic Feb 18 '15 at 08:22
  • But what is the problem? self.quests will be correctly assigned, but if that response is actually what you expect it to be is never checked. (Are you sure it is an Array?) If you want your controller to do something once the quests have been loaded, you can call the controller's method right from within the completion block as well. – Thomas Krajacic Feb 18 '15 at 09:28
-1

You need to make quests a property on your class, so that you can access it from within the async callback.

var quests = NSArray()  

You're aren't going to be able to return the array, because it is async. Just remove the return type. When the callback fires, save the results to your array, and do whatever else you want to happen.

func getQuests(category: NSString, count: Int) {

    Alamofire.request(.GET, apiUrlString, parameters: ["category": category, "count": count])
            .responseJSON { (request, response, json, error) in
                dispatch_async(dispatch_get_main_queue(), {
                    self.quests = json as NSArray
                    println(self.quests)  #=> ()
                })
        }
}
Jeremy Pope
  • 3,342
  • 1
  • 16
  • 17