Whenever I define a function as a variable I lose auto-completion. If say I write code like this:
typealias MyFunction = (_ foo: Int) -> Void
let myFunction: MyFunction = { foo in
print(foo) // shows <<error type>>
}
foo will be <<error type>> in the pop up.
This in Xcode 9.3
Related
There are multiple functions in my app, where each have a different number of parameters
func functionOne(foo: Foo, bar: Bar)
func functionTwo(foo: Foo, bar: Bar, baz: Baz, quux: Quux)
func functionThree(foo: Foo)
The parameter values can vary.
My requirement is to press a button which will run whichever function of the above was run most recently, including its parameter values.
Storing the whole thing (function and parameters) in a variable did not work.
A function and its parameters are stored in a closure. For example:
func f(_ x: Int) {}
func g(_ x: Int, _ y: Int) {}
var saved: () -> Void = { f(1) }
saved() // this executes f(1)
saved = { g(2, 3) }
saved() // now this executes g(2, 3)
You can use #escaping and #autoclosure to store a function and its parameters as a closure in a property of your class and then call it.
Add this class to your project:
// Stored Function Class
class SFC {
static var calledFunc: (() -> Void)?
static func call(_ function: #escaping #autoclosure () -> Void) {
// Store the function
calledFunc = function
// Call it
function()
}
static func reCall() {
// Called the stored function
calledFunc?()
}
// Call this when you no longer want SFC to hold onto your function.
// Your class will not deallocate if you passed in `self` to `call()`
// as long as `calledFunc` retains your function. Setting it to `nil`
// frees it.
static func forget() {
calledFunc = nil
}
}
This is how you use it:
Wrap any function call that you want to repeat with SFC.call(). Call SFC.reCall() to call that function again.
Example:
func add(_ a: Int, _ b: Int) {
print("\(a) + \(b) = \(a + b)")
}
SFC.call(print("hello", "bye"))
SFC.reCall()
SFC.reCall()
SFC.call(add(2, 3))
SFC.reCall()
Output:
hello bye
hello bye
hello bye
2 + 3 = 5
2 + 3 = 5
How does this work?
The contents of the call to call() are automatically wrapped in a closure (that is what #autoclosure does) and passed as function. The #escaping means that you'll be hanging onto that closure after call() returns.
That closure is then assigned to the calledFunc property so that it can be called again later from reCall().
Note: If the function you are passing to call() is a member function, you'll need to explicitly specify self. For example: SFC.call(self.functionThree(foo: fooVar)). Be sure to call SFC.forget() when it's time for your class to be freed so that SFC doesn't hold onto your class instance.
This bit of code crashes the swift (3, 3.1, 4) compiler:
protocol Test {
func f()
}
let x = Test.f // crash
I would expect, perhaps naively, that the compiler would infer x as a Function Type with the signature (Test) -> (Void) -> Void, and that later on, I could call it like so:
let y = SomeClassConformingToTest()
x(y)()
I guess my question is: clearly the compiler should do something other than crash, but should Swift currently support this syntax?
As you say, the compiler should never crash; this is indeed a bug, and has been filed here. In it, Slava Pestov, a member of the Swift team, says:
We plan on making MyProtocol.someInstanceMethod work. You can already
do this for classes, eg,
class Foo {
func f() { ... }
}
let f: Foo -> () -> () = Foo.f
It should have the same behavior for protocols:
protocol Foo {
func f()
}
let f: Foo -> () -> () = Foo.f
I plan on addressing this later.
And the bug report, as of the 8th of May 2017, is now marked as "In Progress", so hopefully this is something that will make it into the release build of Swift 4.0.
However, until implemented/fixed – a simple workaround is just to use a closure expression in order to act as a thunk for the partial application of the method with the instance to call it on:
protocol Test {
func f()
}
struct S : Test {
func f() {
print("hello")
}
}
let x: (Test) -> () -> Void = { $0.f }
let s = S()
x(s)() // "hello"
and of course, if you don't need the intermediate partially applied function, you can just say:
let x: (Test) -> Void = { $0.f() }
let s = S()
x(s) // "hello"
This bit of code crashes the swift (3, 3.1, 4) compiler:
protocol Test {
func f()
}
let x = Test.f // crash
I would expect, perhaps naively, that the compiler would infer x as a Function Type with the signature (Test) -> (Void) -> Void, and that later on, I could call it like so:
let y = SomeClassConformingToTest()
x(y)()
I guess my question is: clearly the compiler should do something other than crash, but should Swift currently support this syntax?
As you say, the compiler should never crash; this is indeed a bug, and has been filed here. In it, Slava Pestov, a member of the Swift team, says:
We plan on making MyProtocol.someInstanceMethod work. You can already
do this for classes, eg,
class Foo {
func f() { ... }
}
let f: Foo -> () -> () = Foo.f
It should have the same behavior for protocols:
protocol Foo {
func f()
}
let f: Foo -> () -> () = Foo.f
I plan on addressing this later.
And the bug report, as of the 8th of May 2017, is now marked as "In Progress", so hopefully this is something that will make it into the release build of Swift 4.0.
However, until implemented/fixed – a simple workaround is just to use a closure expression in order to act as a thunk for the partial application of the method with the instance to call it on:
protocol Test {
func f()
}
struct S : Test {
func f() {
print("hello")
}
}
let x: (Test) -> () -> Void = { $0.f }
let s = S()
x(s)() // "hello"
and of course, if you don't need the intermediate partially applied function, you can just say:
let x: (Test) -> Void = { $0.f() }
let s = S()
x(s) // "hello"
Precedence rules for same name and signature function & closure
When defining a closure and a function with same name (say, foo) and signature, it seems as if the closure takes precedence when calling said (seemingly ambiguous) foo.
// Int -> () function
func foo(num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
/* or...
let foo = { (num: Int) in print("closure \(num)")} */
foo(1) // closure 1
If I option-click the two declarations, they point at each other under the label Related declarations, differing in how they are referred to as:
Declaration: func foo(num: Int)
Related Declarations: foo
...
Declaration: let foo: (Int) -> ()
Related Declarations: foo(_:)
If we attempt to define the same double-foo definitions for a zero-argument function & closure, we get a compile time error prompting invalid redeclaration
// () -> () function
func foo() { print("function")}
// () -> () closure
let foo: () -> () = { print("closure")}
/* or...
let foo = { () in print("closure")} */
/* error: invalid redeclaration of 'foo' */
Question: Why is it that the first case above is considered non-ambiguous, and why does the closure take precedence over the function when calling foo(..) (i.e., in the overload resolution of foo(...)?
I haven't been able to find any official docs (or existing SO thread) explaining this.
Tested for Swift 2.2/Xcode 7.3 and Swift 3.0-dev/IBM Sandbox (with function signature modified to func foo(_ num: Int) { ... }).
In Swift, you cannot name a variable and a function or closure the same thing if no parameters are passed. Although we call them in different ways (foo for a variable and foo() for a function. we will get an invalid redeclaration of foo. The compiler treats the variable as if it has no parameters like the function does. Consider some of these cases:
class X {
var value : Int = 0
func value() -> Int { return 1 } // Invalid redeclaration of 'value()'
}
let x = X()
What is x.value in this case? Is it Int or is it () -> Int? It's legal and useful to treat the methods of classes as if they were closures.
What if we're even more tricky, and do this:
class X {
let value: () -> Int = { 2 }
func value() -> Int { return 1 } // Invalid redeclaration of 'value()'
}
let x = X()
let v = x.value() // ????
Should Swift use the property value and then call it? Or should it call the method value()? Closures are completely legal as properties.
However, using parameters, you can name a function and a variable/Closure the same thing, although I would advise you not to. In this case you should try to name the variable and the function something that describes what they are and/or what they do to make your code more readable by others. I would suggest naming the variable something like
class X {
// Int -> () function
func foo(number num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
}
because by naming the function it will allow user to know exactly which function your are calling when your method and variable names are same. user can know what parameter is for they are passing. by naming when you call the methods like below.
let x = X()
x.foo(number: 2)
x.foo(3)
and you CMD + click it will point you to the exact method or variable/closure you have written for.
consider previous example:
class X {
// Int -> () function
func foo(number num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
}
let x = X()
x.foo(2) // x.foo(num: Int)
x.foo(3) // x.foo
Despite you called the correct method or closure. by cmd + click it will point you to the closure only.
You can drop the code below into playgrounds.
import UIKit
class MyClass {
func foo(a: String, b: () -> ()) {
b()
}
func bar(a: String = "a", b: () -> ()) {
b()
}
}
let object = MyClass()
object.foo("x") { () -> () in
println("foo")
}
object.bar() { () -> () in
println("foo")
}
object.bar() call produces Missing argument for parameter 'b' in call
The question is: am I doing something wrong, or trailing closures are not supported in methods with default parameter values?
It does look like a problem with trailing closures - this code works:
object.bar(b: { () -> () in
println("foo")
})
However if the external name is removed:
func bar(a: String = "a", _ b: () -> ()) {
b()
}
this no longer works:
object.bar({ () -> () in
println("foo")
})
Moreover using a function having a string as the 2nd parameter:
func test( val1: String = "a", val2: String) {
}
the default parameter is correctly assigned, so this succeeds:
test("me")
which is a different behavior than using closures.
Conclusion: a method or function with parameter(s) having default value and a trailing closure does not work if at least one of the parameters with default value is not specified. Avoiding the trailing closure the function works only if the parameter has external name.