Swift function with a function as parameter - swift

I have a question about why I get a compilation error of "Missing return in a function". I am following the examples in the "The Swift Programming Language" book and there is a section about passing a function as a parameter of another function.
Here is the book's example which compiles fine:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition (item) {// anonymous function call
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
I understand this, but I thought I could make a subtle change, because I felt the if condition(item){ } was redundant.
Here is my alteration:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
return condition(item)
}//error here with "Missing return in a function expected to return bool"
}
I'm returning a bool because I return the result of the function. There is no case where I would not return a bool during the for-in loop.
I don't understand why this does not compile, could someone explain why?

First, your change doesn't do what the old code did. Your version returns the result of testing the first element in the list, not whether any of the elements pass the test.
The reason for the error is that your code isn't guaranteed to execute return at all. If the list is empty, then you'll drop to the end of the function without calling return. The compiler is telling you that.

func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return bool
}

Related

How to invoke any swift function conditionally (without if block)

I want to write a general-purpose Swift function that serves the following simple purpose:
Take any function as argument
Take a Bool argument
If the bool argument is TRUE, invoke the input function with its args. Otherwise No-op.
The purpose is to eliminate a lot of clumsy if statements in the code that meet a specific criteria.
Something like:
typealias ClosureType = (Any...) -> Any. // Notice the variable argument of any kind
func invokeIfConditionIsTrue(closure: Closure, condition: Bool) {
if condition {
if let myFunc = closure as? ClosureType {
myFunc()
print("executed")
} else {
print("not executed")
}
}
}
func testIntToInt(i: Int) -> Int {
return i*i
}
func testIntToDouble(i: Int) -> Double {
return Double(i*i)
}
invokeIfConditionIsTrue(testIntToInt, true). // executed
invokeIfConditionIsTrue(testIntToDouble, false). // not executed
However, I am struggling to come up with syntax that will enable the argument passing to the input myFunc() func.
The example is pretty basic, and my input function closure could be accepting and emitting any type of input/outputs, including structs, classes and objective c stuff.
I have a hunch this is possible via a mechanism called function object, but I am not familiar enough with it.
Should I reinvent the wheel, or is there already a library/known way which is doing it successfully, and I am missing out?
I have no idea why you think
invokeIfConditionIsTrue(testIntToInt, condition)
is somehow superior to
if condition { result = testIntToInt(n) }
Or
result = condition ? testIntToInt(n) : 0
but what you want is pretty much impossible unless you wrap the function in a closure because there is no way to express "function with any arguments" in Swift as a type. The best you can do is wrap your function in a closure with known argument types. There's also no general Closure type that represents any closure.
func invokeIfConditionIsTrue(closure: () -> (), condition: Bool) {
if condition {
closure()
print("executed")
}
}
invokeIfConditionIsTrue(closure: { result = testIntToInt(n) }, condition: true)
But, as you can see, that's not really any better than an if statement. In fact, it's much worse.
Another possibility is to define a function that returns a function, but it still needs to know the argument types.
func invokeIfConditionIsTrue(closure: (Int) -> Int, condition: Bool) -> (Int) -> Int?
{
if condition {
return closure
}
else
{
return { _ in 0 } // A dummy function
}
}
invokeConditionIfTrue(closure: testIntToInt, condition: true)(n)
After some haggling and searching for syntax (I wasn't good in FP, as I mentioned in the beginning), I was able to compile and run the below solution in my XCode 14.2 playground.
My desired function:
func executeIfCondition(function: (#escaping (Any...) -> Any), condition: Bool) {
if condition {
function()
}
}
This was supposed to replace following types of calls across my codebase:
if condition {
function()
}
Test functions whom I want to invoke, using executeIfCondition if condition = true.
func printStringAndReturnInt(i: Int, s: String) -> Int {
print(s)
return i
}
func printInt(i: Int) -> Void {
print("\(i)")
}
func printArray(arr: [Int]) -> Void {
arr.forEach({ print("\($0)") })
}
struct Struct1 {
var value1: Int
var value2: Int
}
func printStruct(t: Struct1) {
print("Struct1: \(t.value1) - \(t.value2)")
}
class Class1 {
var value1: Double = 100.0
var value2: Double = 200.0
}
func printClass(c: Class1) {
print("Class1: \(c.value1) - \(c.value2)")
}
Example Usage:
Instead of:
if (true) {
printStringAndReturnInt(i: 5, s: "Wow!")
}
I will now use:
executeIfCondition(function: { _ in printStringAndReturnInt(i: 5, s: "Wow!") }, condition: true)
The rest:
executeIfCondition(function: { _ in printInt(i: 61) }, condition: false)
executeIfCondition(function: { _ in printArray(arr:[9,10,11,12]) }, condition: true)
executeIfCondition(function: { _ in printStruct(t: Struct1(value1: 100, value2: 200)) }, condition: true)
executeIfCondition(function: { _ in printClass(c: Class1()) }, condition: true)
This isn't exhaustive still, but enough for a start.

Why is the function which seems to be returning print declared Void?

The code in question is below:
class Singer {
func playSong() {
print("Shake it off!")
}
}
func sing() -> () -> Void { // Void???
let taylor = Singer()
let singing = {
taylor.playSong()
return
}
return singing
}
let singFunction = sing()
singFunction()
Bonus question: I didn't quite understand declaration:
func sing() -> () -> Void
Couldn't we just declare it as?:
func sing() -> Void
You should read the declaration of sing as:
func sing() -> (() -> Void)
which means that sing returns "a function that returns nothing i.e. () -> Void". sing doesn't return Void, it returns another function. That returned function does whatever it needs to do, and does not return a value, hence Void.
The function being returned here is singing, which calls taylor.playSong().
You could declare sing to return Void, instead of () -> Void, but you'd also have to edit its body:
func sing() {
let taylor = Singer()
taylor.playSong()
}
Since sing can't return another function now, it can only call taylor.playSong() directly.
It doesn't return Void, it returns a closure with no arguments and no return value. This is the same as an anonymous function.

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

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

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

Nested functions error in swift

I am trying to use nested functions in my Swift Program.
func test()->Bool{
func test1()->Bool{
return true
}
func test2()->Bool{
return true
}
func test3()->Bool{
return true
}
return test1 && test2 && test3
}
Its giving me some error! But if i try to
return test1
Its working fine.
Please explain why its giving me error on applying operators in nested function.
There are two approaches to nested functions/closures:
As you have it:
func test() -> Bool {
func test1() -> Bool {
return true
}
func test2() -> Bool {
return true
}
func test3() -> Bool {
return true
}
return test1() && test2() && test3()
}
Or saving it to closures and calling them:
func test() -> Bool {
let test1 = { () -> Bool in
return true
}
let test2 = { () -> Bool in
return true
}
let test3 = { () -> Bool in
return true
}
return test1() && test2() && test3()
}
The difference is really small and it comes down to your own preference but straight from documentation:
Nested functions are closures that have a name and can capture values from their enclosing function.
and
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
Either way, both cases have to be called with () at the end of the name to perform their action, otherwise you're returning a function and not its evaluation. Your return test1 would be only valid in this case:
func test() -> (() -> Bool) {
func test1() -> Bool {
return true
}
return test1
}
where your function's return type is a function that returns Bool after you evaluate it. You want to evaluate though and return the Bool value combined with logical AND statements. Thus, you have to evaluate each function to get each function's Bool value to combine it and return the final value.
return test1 should fail as well, unless you have a test1 property in your class. It seems you are simply forgetting to call these as functions.
return test1() && test2() && test3()
will work just fine.