Why Swift Throws Error When Using Optional Param in Closure Func

Why Swift throws error when using optional param in closure func?

Only functions can have default parameters in Swift, but not closures.

The closure returned from makeIncrementer() has the type
(Int?) -> Int, i.e. it takes one argument of type Int?,
and that must be provided when calling the closure.

Compare also SR-531 Allow default parameters in closure parameters, in particular the comment

Doesn't really make sense, if you need this behavior you should be using a function. Closures are usually called anonymously with their arguments all provided, so there is no need for default parameter values.

parameters with optional closures in swift

Firstly, to use closures as an argument for a function, you should declare them like so:

func myFunc(closure: (Int) -> Void) {
// Now I can call closure like so:
let myInt = 10
closure(myInt)
}

(As pointed out by @Airspeed Velocity, the parenthesis around Int are not strictly required because there is only one argument. Whether you include them is just personal preference)

Secondly, you can modify the previous function to include an optional closure, as follows:
(Note the ? and parenthesis around the closure that indicate the closure is an optional, not the return type)

func myFunc(closure: ((Int) -> Void)?) {
// Now when calling the closure you need to make sure it's not nil.
// For example:
closure?(10)
}

Thirdly, to add a default value of nil, which is what it looks like you're trying to do with the = {} on the end of YesClosure: ()->() = {}, you could do:

func myFunc(closure: ((Int) -> Void)? = nil) {
// Still need to make sure it's not nil.
if let c = closure {
c(10)
}
}

Finally, just as a note, you can set the names of the arguments of the closure, which can make it easier to identify what you're passing to the closure when calling it. For example:

(Note - here parenthesis are required around value: Int)

func myFunc(closure: ((value: Int) -> Void)) {
closure(value: 10)
}

Even more finally, you could use typealias. According to the documentation:

A type alias declaration introduces a named alias of an existing type into your program.

Here's an example of how to use it with a closure:

typealias MyClosureType = () -> Void

func myFunc(closure: MyClosureType) {
closure()
}

Hope that helps!

Swift optional escaping closure parameter

There is a SR-2552 reporting that @escaping is not recognizing function type alias. that's why the error @escaping attribute only applies to function types. you can workaround by expanding the function type in the function signature:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
print(stuff)
action = completion
completion?()
}

func doStuffAgain() {
print("again")
action?()
}

doStuff(stuff: "do stuff") {
print("swift 3!")
}

doStuffAgain()

EDIT 1::

I was actually under a xcode 8 beta version where the bug SR-2552 was not resolved yet. fixing that bug, introduced a new one(the one you're facing) that is still open. see SR-2444.

The workaround @Michael Ilseman pointed as a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping.

func doStuff(stuff: String, completion: Action?) {...}

EDIT 2::

The SR-2444 has been closed stating explicitly that closures in parameters positions are not escaping and need them to be marked with @escaping to make them escaping, but the optional parameters are implicitly escaping, since ((Int)->())? is a synonyms of Optional<(Int)->()>, optional closures are escaping.

Swift Closure why does calling function return error?

First of all you are getting error here printerFunction(2) because printerFunction can not take any argument and If you want to give an argument then you can do it like:

func printerFunction(abc: Int) -> (Int) -> (){


}

And this will work fine:

printerFunction(2)

After that you are giving reference of that function to another variable like this:

let printAndReturnIntegerFunc = printerFunction() 

which means the type of printAndReturnIntegerFunc is like this:

Sample Image

that means It accept one Int and it will return void so this will work:

printAndReturnIntegerFunc(2)

Throw Error in function taking escaping closure

I've created test function for demonstration and everything works properly

// error type
enum TestError: Error { case notFound, empty }
// define typealias for eiser usage of complex function type
typealias ThrowableCallback = () throws -> Bool

func createAccount(_ shouldThrow: Bool, completion: @escaping (_ inner: ThrowableCallback) -> Void) {
// call completion callback asynchronously
DispatchQueue.main.async {
if shouldThrow {
// throw error
completion({ throw TestError.notFound })
}
// return value
completion({ return true })
}
}

Usage:

createAccount(true) { (inner: ThrowableCallback) -> Void in
do {
let success = try inner()
print(success)
} catch {
print(error)
}
}

UPD: I don't recommend using this technique for handling errors in asynchronous functions. Use separate callbacks for success and failure or Promises to gracefully handle asynchronous code (check this out for more info)

UPD 2: Actual Solution

typealias ThrowableCallback = () throws -> User 
func createAccount(_ userModel: UserModel,
_ password: String,
completion: @escaping (_ inner: ThrowableCallback) -> Void) {
Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in
if let error = error { completion({ throw error }) }
else { completions({ return user! }) }
})
}

// usage
createAccount(userModel, somePassword) { (inner: ThrowableCallback) -> Void in
do {
let createdUser = try inner()
} catch {
ler errCode = (error as NSError)._code
switch errCode {
case .emailAlreadyInUse:
showAlert("Email is already in use")
case .weakPassword:
showAlert("please enter stronger password ")
case .networkError:
showAlert("it seams that there is no internet connection")
default:
showAlert("Error creating user. Please try again later")
}
}
}

Throw from a trailing closure in Swift


func innerClosure(a: String, completion: (String) throws ->Void) {
try? completion(a + ", World")
}

does not throw an error because try? is an “optional try”: It returns an Optional which is nil in the case of an error. Here the return value is ignored, so that errors are ignored silently.

What you want is

func innerClosure(a: String, completion: (String) throws ->Void) rethrows {
try completion(a + ", World")
}

which makes innerClosure() a throwing function only if called with a throwing parameter.

func iCanThrow() can also be marked as rethrows instead of throws because it does not throw an error “on its own.”
See also What are the differences between throws and rethrows in Swift?.



Related Topics



Leave a reply



Submit