Returning after calling completion handler, or return completion handler parameter? - swift

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?

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.

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)

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)
}
}
}

Related

Can I do something not `transfrom` in `map` and `flatMap` function?

Is Map function of Optional in Swift just used to transform?
If I want do something just Optional has some value, Can I Use map function? if not, why?
According to apples examples, we used map like this
let possibleNumber: Int? = Int("42")
possibleNumber.map { $0 * $0 }
Can I use it like this? : (If it's not proper, how to explain it)
func setImage(with data: Data?) {
data.flatMap { UIImage(data: $0) }
.map { imageView.image = $0 }
}
Furthermore map function should return a value, but why this function does not have any warning about not used of result ( such as result of call map{...} is unused ) ?
You certainly can do it, it's just not very conventional. When people see map, they have a pre-conceived expectation that it'll be doing a transformation. You're violating that expectation, but there's nothing technically "wrong" about it.
Personally, I prefer using this extension:
extension Optional {
/// An enum used to ensure that `ifNone` is never called before `ifSome`.
enum IfSomeResult {
case some
case none
func ifNone(_ closure: () throws -> Void) rethrows -> Void {
switch self {
case .some: return
case .none: try _ = closure()
}
}
}
#discardableResult
func ifSome(then closure: (Wrapped) throws -> Void) rethrows -> IfSomeResult {
if let wrapped = self {
try _ = closure(wrapped)
return IfSomeResult.some
}
else {
return IfSomeResult.none
}
}
func ifNone(then closure: () throws -> Void) rethrows -> Void {
if case nil = self { try _ = closure() }
}
}
And writing code like:
data.flatMap { UIImage(data: $0) }
.ifSome { imageView.image = $0 }
Why doesn't it warn about an unused value?
The closure is inferred to return Void (the empty tuple type, whose only value is the empty tuple, ()). The compiler never emits warnings about Void values being unused`. Here's an example:
Optional(123).map { _ in () }

How do I reverse a promise?

I'm using PromiseKit to handle flow through a process.
Prior, I did a similar app without promises but decided frick it I'm gonna try promises just because, well, why not?
So I'm throwing a back button in the mix as I did in the prior app. Only problem is, I'm not exactly sure how to handle "reversing" if you want to call it that.
So say I have a flow of
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// wrap it up, boss
}.catch {
// you're an idiot, bud
}
Say I'm in the first or second part of the chain then and I want to go back up the chain - is this possible?
Is there a link y'all can give me that I can use to read up on how to do that?
I'm thinking I might have to restart the "chain", but then how would I step through the flow....WAIT (light bulb), I can programmatically fulfill the necessary promises with whatever the data is that initially was fulfilled with until I get to the point in the "chain" where I needed to go back to, right?
Advice D:?
You can always have a catch and a then on the same promise.
var somePromise = doSomething()
// first chain
somePromise.catch { error in
// handle error
}
// second chain from the same starting point
somePromise.then {
// do something else
}.then {
// do something else
}.catch {
// you can still catch the error here too
}
You're basically creating two promise chains from the same original promise.
No, you can not do that. Once you commit a promise, you can not reverse that. Because the chain is supposed to finish in the descending order, it's cumbersome to track the order in each .then block.
What you can do is, handle the internal logic responsible to fulfill or reject a promise and start the chain from the beginning.
func executeChain() {
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// condition to
executeChain()
}.catch {
// you're an idiot, bud
}
}
func doSomething() -> Promise<SomeThing>{
if (condition to bypass for reversing) {
return .value(something)
}
// Normal execution
}
But if you can improve your question with an actual use case and code then it could help providing more suitable explanation.
No you can't but you can set order in array.
bar(promises: [foo1(), foo2(), foo3()])
func bar<T>(promises: [Promise<T>]) {
when(fulfilled: promises)
.done { _ in
// TODO
}
.catch { error in
// When get error reverse array and call it again
self.bar(promises: promises.reversed())
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
or alternatively
bar(foo1, foo2, foo3)
.done { _ in
// TODO
}
.catch { error in
print(error.localizedDescription)
self.bar(self.foo3, self.foo2, self.foo1)
.done { _ in
// TODO
}
.catch { error2 in
print(error2.localizedDescription)
}
}
func bar<T>(_ promise1: () -> Promise<T>,
_ promise2: #escaping () -> Promise<T>,
_ promise3: #escaping () -> Promise<T>) -> Promise<T> {
return Promise { seal in
promise1()
.then { _ in return promise2() }
.then { _ in return promise3() }
.done { model in
seal.fulfill(model)
}
.catch {
seal.reject($0)
}
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}

Swift: async execution in series + array of closures + escaping

I have a few async function which I'd like to run in series to avoid "callback hell" and simplify things I've written the following helper structure:
typealias AsyncCallback = (Bool) -> Void
typealias AsyncFunction = (AsyncCallback) -> Void
public struct AsyncHelpers {
public static func series(_ functions: [AsyncFunction], _ callback: #escaping AsyncCallback) {
var index = 0
var completed: AsyncCallback? = nil
completed = { success in
if success == false { callback(false); return }
index += 1
if index < functions.count {
functions[index](completed!)
return
}
callback(true)
}
functions[index](completed!)
}
}
AsyncHelpers.series([
{ callback in
// do async stuff
callback(true)
}, { callback in
// then do async stuff
callback(true)
}
]) { callback in
// when all completed
}
I can set the #escaping attribute for completion handler, but when I try to apply this attribute to [AsyncFunction] compilator fails with this error: "#escaping attribute only applies to function types". Should I mark these closured as escaping in other way, please?
What is lifecycle of index variable, can I use it inside completed closure without any problem?

PromiseKit how to return "when(resolved)" as a promise?

I have a series of functions that look like this:
func getA() -> Promise<Void> {
// code
}
func getB() -> Promise<Void> {
// code
}
func getC() -> Promise<Void> {
// code
}
I want to return a Promise when all of these are finished. Here's what I tried:
func getStuff() -> Promise<[Result<Void>]> {
return when(resolved: [getA(), getB(), getC()])
}
But I'm getting a compile error: 'Result' is ambiguous for type lookup in this context. How can I achieve this?
func getStuff() -> Promise<[PromiseKit.Result<Void>]> {
return when(resolved: [getA(), getB(), getC()])
}
There are several things named Result in your code and you need to tell Swift that Result in this case refers to PromiseKit.Result or to use Resolution assuming it's not taken in the namespace and you don't care about the related ErrorConsumptionToken.
func getStuff() -> Promise<[Resolution<Void>]> {
return when(resolved: [getA(), getB(), getC()])
}

Understanding syntax of closure with no return type

I am getting into swift for the first time here and I have stumbled across a closure statement that doesn't make a whole lot sense to me based off of my current understanding of how closures are written. This is really a two-part question because I also don't quite grasp the intention/interpretation behind the conditional binding with this closure.
The chunk of code I am thrown by is this:
FIRAuth.auth()?.createUser(withEmail: email, password: password) {
(user, error) in if let error = error {
print(error.localizedDescription)
return
}
}
My understanding is that the closure needs specify a return type based off of the docs definition (something1, something2) -> () so from the bit of code above, does that just mean that swift can infer a void return by not including the -> ()?
My assumption is that the conditional binding statement just says 'if an error arg is passed into this closure, then print the error?
Please use as much detail as possible in your explanation as possible so I can further my understanding. Cheers!
The following are all equivalent
func foo1() -> () { }
func foo2() -> () { return () }
func foo3() -> () { return Void() }
func foo4() -> () { return }
func foo5() -> Void { }
func foo6() -> Void { return () }
func foo7() -> Void { return Void() }
func foo8() -> Void { return }
func foo9() { }
func foo10() { return () }
func foo11() { return Void() }
func foo12() { return }
print(type(of: foo1)) // (()) -> ()
// ...
print(type(of: foo5)) // (()) -> ()
// ...
print(type(of: foo9)) // (()) -> ()
// ...
If no return type is supplied to a function(/closure), then the empty tuple type (which is typealiased by Void) is inferred. We may, however, explicitly supply this return type, either as () (the type), or Void. From the language guide - functions:
Functions Without Return Values
Functions are not required to define a return type. ...
...
Strictly speaking, this version of the greet(person:) function does
still return a value, even though no return value is defined.
Functions without a defined return type return a special value of
type Void. This is simply an empty tuple, which is written as ().
Conversely, if no return is given at the end of a function block, it will be the same as explicitly returning an instance of the empty tuple, namely () (the value). This may also be written simply as return.
() is the empty tuple. Void in Swift is just a type alias to (). If a function or closure doesn't specify a return type, then () is assumed.
To avoid confusion, the (user, error) in should be on its own line:
FIRAuth.auth()?.createUser(withEmail: email, password: password) { user, error in
if let error = error {
print(error.localizedDescription)
return
}
}
I'd suggest using a guard statement to enforce an early return:
FIRAuth.auth()?.createUser(withEmail: email, password: password) { user, error in
guard error == nil else {
print(error!.localizedDescription)
return
}
// There's no error, carry on...
}