In this question I saw today It defines a struct Effect that has a property run that is a closure that takes a Generic parameter:
struct Effect<T> {
let run: (#escaping (T) -> Void) -> Void
}
Then the sample code creates an instance of Effect<Int>, and specifies the closure for the run property with something that looks like trailing closure syntax:
let anIntInTwoSeconds = Effect<Int> { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
What makes that legal? I would expect to need to specify the run parameter explicitly in a call to the init method:
let anIntInTwoSeconds = Effect<Int>(run: { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
)
Either version compiles and works. What in Swift makes that first version legal? I couldn't figure out how to frame the question so that I could search for an answer.
This works just like any function whose last parameter is a function. Trailing closure syntax is trailing closure syntax. The fact that the function is an initializer changes nothing.
So let me build up to it in stages. You know you can say:
func myfunc(whatever: () -> ()) {}
myfunc {}
Okay, but now let's make it a static method:
struct S {
static func myfunc(whatever: () -> ()) {}
}
S.myfunc {}
OK, but init is a static method — it's just a static method whose name you are allowed to omit:
struct S {
let whatever: () -> ()
}
S {} // meaning S.init {}
Related
I'm stumped by a bit of Swift struct syntax.
For a regular struct, I understand how to define and initialize it.
struct Thing {
let name: String
}
let aThing = Thing(name: "The Name")
But I was reading a bit about functional programming and came across this syntax and what stumped me most was the initialization.
struct Effect<T> {
let run: (#escaping (T) -> Void) -> Void
}
// What is "callback" here? How does this work?
let anIntInTwoSeconds = Effect<Int> { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
anIntInTwoSeconds.run { theInt in
print(theInt)
}
If I were to see that with a "normal" initializer, I would understand it, but what is happening on this line? What is this syntax called so I can research it further?
let anIntInTwoSeconds = Effect<Int> { callback in
The declaration for the Effect type
struct Effect<T> {
Defines Effect as using a Generic type T. So you can create Effect objects that work on different types.
The line
let anIntInTwoSeconds = Effect<Int> { callback in
Creates an Effect object that operates on Ints, and assigns it to the variable anIntInTwoSeconds.
The { callback in part defines the closure for the Effect object. That declaration could be rewritten like this:
let anIntInTwoSeconds = Effect<Int>(run: { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
)
You can interpret as:
'Create an instance of the Effect struct, with a run property that contains the closure that begins with "{ callback in"...'
(It's confusing because the run property is, itself, a closure. So we create an instance of Effect, and pass it a "run" value that's a closure.)
The "callback in" part, specifically, tells the compiler that, inside that closure, the parameter to the closure should be named "callback".
Closures As Parameters
let driving = {
print("I'm driving in my car.")
}
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
travel(action: driving)
action is a parameter label. How come we consider it as a function call as in action()?
Lets look at a brief example:
func myFunction() {...} Swift sees this as () -> Void
the same way that Swift sees this "John" as String
So when you write a function like myFunction you could really say
func myFunction() -> Void {...} for the same result.
Now action is defined as a parameter as a function but the type that it accepts for that parameter is () -> Void aka a closure which for now you can think of just another function.
So the line action() is just the call of that function.
But be sure to read up on functions accepted as parameters
I'm trying to declare an argument in Swift that takes an optional closure. The function I have declared looks like this:
class Promise {
func then(onFulfilled: ()->(), onReject: ()->()?){
if let callableRjector = onReject {
// do stuff!
}
}
}
But Swift complains that "Bound value in a conditional must be an Optional type" where the "if let" is declared.
You should enclose the optional closure in parentheses. This will properly scope the ? operator.
func then(onFulfilled: ()->(), onReject: (()->())?){
if let callableRjector = onReject {
// do stuff!
}
}
To make the code even shorter we can use nil as default value for onReject parameter and optional chaining ?() when calling it:
func then(onFulfilled: ()->(), onReject: (()->())? = nil) {
onReject?()
}
This way we can omit onReject parameter when we call then function.
then({ /* on fulfilled */ })
We can also use trailing closure syntax to pass onReject parameter into then function:
then({ /* on fulfilled */ }) {
// ... on reject
}
Here is a blog post about it.
Since I assume, that this "optional" closure should simply do nothing, you could use a parameter with an empty closure as default value:
func then(onFulfilled: ()->(), onReject: ()->() = {}){
// now you can call your closures
onFulfilled()
onReject()
}
this function can now be called with or without the onReject callback
then({ ... })
then({ ... }, onReject: { ... })
No need for Swift's awesome Optionals? here!
Maybe it's a cleaner way. Specially when the closure has complicated parameters.
typealias SimpleCallBack = () -> ()
class Promise {
func then(onFulfilled: SimpleCallBack, onReject: SimpleCallBack?){
if let callableRjector = onReject {
// do stuff!
}
}
}
As an alternative to creating a TypeAlias or using well-placed parentheses, there's always Swift's Optional type itself.
It can be used just like your typical Java (or in this case Swift) Generic Array syntax Optional<() -> ()>
OR in context:
func callAClosure(firstClosure: () -> (), secondClosure: Optional<() -> ()> {
if let secondClosure = secondClosure {
secondClosure()
}
else { firstClosure() }
}
I find this to be fairly clean. As a bonus, in the context of SwiftUI where generics can be common, like struct CommonList<T: View>: View then you don't have to create a typealias that only gets used once (commonly the init function for that struct). Instead, you make one simple optional closure parameter, and you're all done!
Hopefully this helps anyone that runs into the issue and happy coding!
Compiler error Closure use of non-escaping parameter 'completion' may allow it to escape, Which make sense because it will be called after the function return.
func sync(completion:(()->())) {
self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
completion()
}
}
But if I make closure optional then no compiler error, Why is that? closure can still be called after the function returns.
func sync(completion:(()->())?) {
self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
completion?()
}
}
Wrapping a closure in an Optional automatically marks it escaping. It's technically already "escaped" by being embedded into an enum (the Optional).
Clarification:
For understanding the case, implementing the following code would be useful:
typealias completion = () -> ()
enum CompletionHandler {
case success
case failure
static var handler: completion {
get { return { } }
set { }
}
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler.handler = handlerParameter
}
At the first look, this code seems to be legal, but it's not! you would get compile-time error complaining:
error: assigning non-escaping
parameter 'handlerParameter' to an #escaping closure
let chObject = CompletionHandler.handler = handlerParameter
with a note that:
note: parameter 'handlerParameter' is implicitly non-escaping func
doSomething(handlerParameter: completion) {
Why is that? the assumption is that the code snippet has nothing to do with the #escaping...
Actually, since Swift 3 has been released, the closure will be "escaped" if it's declared in enum, struct or class by default.
As a reference, there are bugs reported related to this issue:
Optional closure type is always considered #escaping.
#escaping failing on optional blocks.
Although they might not 100% related to this case, the assignee comments are clearly describe the case:
First comment:
The actual issue here is that optional closures are implicitly
#escaping right now.
Second comment:
That is unfortunately the case for Swift 3. Here are the semantics for
escaping in Swift 3:
1) Closures in function parameter position are
non-escaping by default
2) All other closures are escaping
Thus, all generic type argument closures, such as Array and Optional, are escaping.
Obviously, Optional is enum.
Also -as mentioned above-, the same behavior would be applicable for the classes and structs:
Class Case:
typealias completion = () -> ()
class CompletionHandler {
var handler: () -> ()
init(handler: () -> ()) {
self.handler = handler
}
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}
Struct Case:
typealias completion = () -> ()
struct CompletionHandler {
var handler: completion
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}
The two above code snippets would leads to the same output (compile-time error).
For fixing the case, you would need to let the function signature to be:
func doSomething( handlerParameter: #escaping completion)
Back to the Main Question:
Since you are expecting that you have to let the completion:(()->())? to be escaped, that would automatically done -as described above-.
I have an extension Array in the form of:
extension Array
{
private func someFunction(someClosure: (() -> Int)?)
{
// Do Something
}
func someOtherFunction(someOtherClosure: () -> Int)
{
someFunction(someClosure: someOtherClosure)
}
}
But I'm getting the error: Passing non-escaping parameter 'someOtherClosure' to function expecting an #escaping closure.
Both closures are indeed non-escaping (by default), and explicitly adding #noescape to someFunction yields a warning indicating that this is the default in Swift 3.1.
Any idea why I'm getting this error?
-- UPDATE --
Screenshot attached:
Optional closures are always escaping.
Why is that? That's because the optional (which is an enum) wraps the closure and internally saves it.
There is an excellent article about the quirks of #escaping here.
As already said, Optional closures are escaping. An addition though:
Swift 3.1 has a withoutActuallyEscaping helper function that can be useful here. It marks a closure escaping only for its use inside a passed closure, so that you don't have to expose the escaping attribute to the function signature.
Can be used like this:
extension Array {
private func someFunction(someClosure: (() -> Int)?) {
someClosure?()
}
func someOtherFunction(someOtherClosure: () -> Int) {
withoutActuallyEscaping(someOtherClosure) {
someFunction(someClosure: $0)
}
}
}
let x = [1, 2, 3]
x.someOtherFunction(someOtherClosure: { return 1 })
Hope this is helpful!
The problem is that optionals (in this case (()-> Int)?) are an Enum which capture their value. If that value is a function, it must be used with #escaping because it is indeed captured by the optional.
In your case it gets tricky because the closure captured by the optional automatically captures another closure. So someOtherClosure has to be marked #escaping as well.
You can test the following code in a playground to confirm this:
extension Array
{
private func someFunction(someClosure: () -> Int)
{
// Do Something
}
func someOtherFunction(someOtherClosure: () -> Int)
{
someFunction(someClosure: someOtherClosure)
}
}
let f: ()->Int = { return 42 }
[].someOtherFunction(someOtherClosure: f)