1
votes

I have a method that can be summed up to look like this:

func apply(username: String, email: String, password: String,
           onDone: @escaping (_ error: Error?) -> ())
{
    //Do async stuff
    do
    {
        try asyncGood()
        onDone(nil)
        return
    }
    catch
    {
        onDone(error)
        return
    }
}

What's the difference between doing:

return onDone(error)

versus

onDone(error)
return

?
Does it just save an extra line of code? I don't see any difference between the two. Am I missing some fundamental aspect of asynchronous Swift programming?

In your opinion, should I always try to condense everything down such that onDone(...) only gets called once at the end?

3
Many people are saying return statements aren't necessary at all, and I get that. But when there are multiple code paths that call onDone(...), I've found that it gets called multiple times which calls subsequent code multiple times. So, that's why I always terminate my onDone(...)s with return statements out of habit. - Xavier L.

3 Answers

2
votes

Semantically, both cases are the same. You are basically saying:

return ()

Your method is declared to return (), and since the onDone closure also returns a (), you can say return onDone(error). The return types match.

However, I find writing them in 2 separate lines more readable:

// This clearly shows that you are doing 2 separate things
onDone(error) // call the completion handler
return // return

Or even omit the return!

onDone(error)
// there will be an implicit return at the end of the method.
1
votes

Both are same. apply function return type is Void and onDone closure return type is also Void. So both are same.

return onDone(error)

or

onDone(error)
return

or you can just ignore return because return type is Void

onDone(error)
1
votes

There is no difference. In fact, there is no need for return keyword at all.

For swift all the following declaration is equivalent:

func doNothing() {
}

func doNothing2() -> Void {
}

func doNothing3() -> () {
}

func doNothing4() {
    return ()
}

func doNothing5() -> Void {
    return ()
}

When you return () you return nothing. No return is exactly the same as return nothing. Functions returning Void may be equivalently used as following

doNothing()

var result = doNothing()

More, Void can also be used as a type parameter which is a very powerful feature:

func genericCall<T>(_ f: () -> T) -> T {
    return f()
}

var result1 = genericCall { print("test") } // result1 is Void
var result2 = genericCall { return 5 } // result2 is Int

Answering your initial question, I would suggest to omit return at all

func doStuffAsync(_ callback: @escaping (Error?) -> Void) {
    // Just an example. Could be any other async call.
    DispatchQueue.main.async {
        do {
            try doStuff()
            callback(nil)
        }
        catch {
            callback(error)
        }
    }
}