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.
Related
I'm trying to create a function that has a closure. Nothing is passed to the function, just after it finishes another function has to be called. Something like this:
func addGradient(closure: syntax) {
(closure: _ in ("function call here") )}
so it can be called similiar to this
addGradient(closure: "function to be called")
Just use the signature for the function minus any of the names:
func doIt(one: Int, two: String) -> [String] {
…
}
Would have the closure signature of:
(Int, String) -> [String]
So yours would be:
func addGradient(closure: (Int, String) -> [String]) {
…
}
And you can call it like this:
addGradient(closure: doIt)
One more note, a function like this:
func doAgain() {
…
}
Has a closure signature of this:
() -> ()
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.
Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}
NOTE: This question was asked while Swift 2.1 was the latest.
Given:
class IntWrapper {
var i: Int = 1
}
func add(inout m: Int, i: Int) {
m += i
}
And a higher order function
func apply() -> (inout i: Int) -> () -> () {
return { (inout i: Int) -> () -> () in
return {
add(&i, i: 1)
}
}
}
Application as so results in the member variable value never changing:
var intW = IntWrapper()
print(intW.i) // Prints '1'
apply()(i: &intW.i)()
print(intW.i) // Prints '1'
However, when changing the function to curried form
func apply()(inout i: Int)() -> () {
add(&i, i: 1)
}
Application results in the member variable value changing:
var intW = IntWrapper()
print(intW.i) // Prints '1'
apply()(i: &intW.i)()
print(intW.i) // Prints '2'
I'm curious to why this is. I always thought that curried syntax was sugar for the higher ordered form of the function, but apparently there are semantic differences.
Additionally this accepted proposal to remove curried syntax from swift seems to state this is merely sugar, and I'm concerned we're losing more than syntax by removing this from the language. Is there a way to get the same functionality from a higher order function?
How can I store an array of functions to callback later in an array like in JavaScript? Any and AnyObject type cannot hold functions with different types of method signatures.
You can use an enum to put various functions into the Array and then extract the functions with a switch.
enum MyFuncs {
case Arity0 ( Void -> Void )
case Arity2 ( (Int, String) -> Void)
}
func someFunc(n:Int, S:String) { }
func boringFunc() {}
var funcs = Array<MyFuncs>()
funcs.append(MyFuncs.Arity0(boringFunc))
funcs.append( MyFuncs.Arity2(someFunc))
for f in funcs {
switch f {
case let .Arity0(f):
f() // call the function with no arguments
case let .Arity2(f):
f(2,"fred") // call the function with two args
}
}
Note: this answer is for Swift versions 1.0 and lower.
Functions that have different parameters and return types are of a different type so they can't be stored in an array together. They also don't conform to the Any or AnyObject protocols.
If you have functions with the same parameters though you can work around that. Even though the functions below return a tuple of Double and an Int, they can both be defined as () -> Any function types.
func func1 () -> Int {
return 1
}
func func2 () -> (Double, Double){
return (2, 3)
}
var a: () -> Int = func1
var b: () -> (Double, Double) = func2
var arr: Array< () -> Any> = [a, b]
Below is an example with both an array and a dictionary. Tested and working in Xcode 6.1 (6A1046a). Note that functions from dictionaries must first be unwrapped.
This technique does however fall apart when the functions have different parameter or return types, for the reasons explained by connor in his answer.
class MyViewController: UIViewController
{
let arrayOfFunctions = [function1, function2]
let dictionaryOfFunctions = [
"function1": function1,
"function2": function2
]
func function1() {
NSLog("function1")
}
func function2() {
NSLog("function2")
}
override func viewDidLoad()
{
let fn1 = arrayOfFunctions[0]
fn1(self)()
let fn2 = dictionaryOfFunctions["function2"]
fn2!(self)()
}
}
As of Swift 1.1, all function types conform to Any, so you can hold functions in an Any array.
func foo (str: String) -> Int {
return 1
}
func bar () -> (Double, Double){
return (2, 3)
}
var a: Any = foo
var b: Any = bar
var arr: Any = [a, b]
A simpler approach to call stored function in array on demand , to use parameters a simple workaround is to make dict args and use it inside the function.
var args = [ "a" : 1, "b" : 2 ]
var requestQueue : [() -> Void] = []
func a() -> Void {
let param = args["a"]
print(param!)
}
func b() -> Void {
let param = args["b"]
print(param!)
}
requestQueue.append(a)
requestQueue.append(b)
for item in requestQueue {
item() //calling the functions
}