39

I have been skimming the StackOverflow questions trying to figure out where I'm going wrong with my code, but I just can't seem to! I am trying to convert my Swift 1.2 project to Swift 2.0, and am having an issue with my class that downloads JSON data.

I am continually receiving the error Unexpected non-void return value in void function.

Here is the code, somewhat truncated, that I am using;

...

class func fetchMinionData() -> [Minion] {

    var myURL = "http://myurl/test.json"

    let dataURL = NSURL(string: myURL)

    let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)

    let session = NSURLSession.sharedSession()

    session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
        let minionJSON = JSON(data!)

        var minions = [Minion]()

        for (_, minionDictionary) in minionJSON {
            minions.append(Minion(minionDetails: minionDictionary))
        }

        return minions
        //THIS IS WHERE THE ERROR OCCURS

    }).resume()
}

...

Perhaps I am overlooking something simple, but I'm unsure why my function would be considered void at all. Any thoughts would be immensely appreciated! Thank you!

whyceewhite
  • 6,317
  • 7
  • 43
  • 51
ZbadhabitZ
  • 2,753
  • 1
  • 25
  • 45

2 Answers2

57

You have a problem because your line:

return minions

does not return from your function. Instead, it returns from the completion handler in dataTaskWithRequest. And it shouldn't be doing so because that closure is a void function.

The problem which you have results from the fact that dataTaskWithRequest is an asynchronous operation. Which means that it can return later after executing your function.

So, you need to change your design pattern.

One way of doing that would be the following:

static var minions:[Minion] = [] {
    didSet {
        NSNotificationCenter.defaultCenter().postNotificationName("minionsFetched", object: nil)
   }
}



class func fetchMinionData() {

    var myURL = "http://myurl/test.json"
    let dataURL = NSURL(string: myURL)
    let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)

    let session = NSURLSession.sharedSession()

    session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
        let minionJSON = JSON(data!)

        var minions = [Minion]()

        for (_, minionDictionary) in minionJSON {
            minions.append(Minion(minionDetails: minionDictionary))
        }

        self.minions = minions
        //THIS IS WHERE THE ERROR OCCURS

    }).resume()
}

Then before calling your function you should register to listen for NSNotification with name "minionsFetched". And only after you get that notification you should process the minions as if they were fetched.

yesthisisjoe
  • 1,987
  • 2
  • 16
  • 32
Andriy Gordiychuk
  • 6,163
  • 1
  • 24
  • 59
  • That makes sense to me. But if I move the `return minions` line to after `}).resume()`, which is where I think it should go, I receive the error **Use of unresolved identifier minions**. Which I can't seem to grasp how to resolve! – ZbadhabitZ Aug 20 '15 at 14:47
  • thanks @AndriyGordiychuk Gordiychuk for the very important line You have a problem because your line: return minions does not return from your function. Instead, it returns from the completion handler in dataTaskWithRequest – Manish Singh Jul 15 '16 at 04:39
24

I fixed mine by creating a completion handler. You can do this instead of using notifications:

class func fetchMinionData(completionHandler: (minions: [Minion]) -> Void) {

    var myURL = "http://myurl/test.json"

    let dataURL = NSURL(string: myURL)

    let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)

    let session = NSURLSession.sharedSession()

    session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
        let minionJSON = JSON(data!)

        var minions = [Minion]()

        for (_, minionDictionary) in minionJSON {
            minions.append(Minion(minionDetails: minionDictionary))
        }

        completionHandler(minions: minions)
        //THIS IS WHERE YOUR PREVIOUS ERROR OCCURRED

    }).resume()
}
jaytrixz
  • 4,059
  • 7
  • 38
  • 57
  • 1
    Way to revive a 4-year-old answer, but don't we have to make the closure `@escaping` since the function scope will be completed before the async call? – Mango Sep 08 '20 at 07:20