Check if a variable is a block / function / callable in Swift

Is there a simple and definite way in Swift to check if something is a block / function being called? In some languages, this is a trivial thing, but maybe I look at it from the wrong point of view in Swift? Consider the following.

func foo(){ print("foo") } var bar: () -> () = { print("bar") } var baz: () -> (Bool) = { print("baz"); return true } print(foo) // (Function) print(bar) // (Function) print(baz) // (Function) print(foo is () -> ()) // true print(bar is () -> ()) // true print(baz is () -> ()) // false print(baz is () -> (Bool)) // true 

Swift knows that they are all functions, although there is no such type of data. I can verify using a solid signature, but there may be a situation where I do not need a signature * and just want to call it. For instance:

 func call(callable: () -> ()) { callable() } call(foo) // foo call(bar) // bar call(baz) // error: cannot convert value of type '() -> (Bool)' to expected argument type '() -> ()' 

I can rewrite it in such a way that it will work for the return types of the Void and Bool types, but doing it for each type is crazy, especially since I do not care, but the compiler does ...

 func call(callable: Any) { if let block: () -> () = callable as? () -> () { block() } else if let block: () -> (Bool) = callable as? () -> (Bool) { block() } } call(foo) // foo call(bar) // bar call(baz) // truely baz 

* Agree, not caring about the signature, this is a sin. For argumentation, let's just not care about the return type.

+6
source share
1 answer

You can check the string representation of the .dynamicType called for the existence of the substring -> . Not very elegant, but it works:

 func isAClosure<T>(foo: T) -> Bool { return String(foo.dynamicType).containsString("->") } var a : () -> () = { print("Foobar") } var b : (Double) -> (Bool) = { $0 > 0 } var c : Int = 1 isAClosure(a) // true isAClosure(b) // true isAClosure(c) // false 

Of course, as Marcus Rossel points out in the comment above, you still don’t know anything about the parameters of the callee (but perhaps this may be the next step to find out, given that you know that it is the callee).


Addendum regarding OPs questions below: just a technical discussion and not recommended methods.

You use the same approach as above to check if a function argument is a closure without arguments ( () -> (...) ) or one of them contains neither arguments nor return type ( () -> () ) etc. Using this approach, you can define a generic function that calls the argument sent to the function only if it has a certain type of closure. For this in-function-call, you will need to use the type conversion to the expected closure type, as described above in your Q. It will probably be difficult to get around this wrt non-generic approach by calling closure. The following are a few examples.

 /* Example functions */ func isAVoidParamClosure<T>(foo: T) -> Bool { let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") return bar.count > 1 && (bar.first?.characters.count ?? 0) == 2 } func callIfVoidVoidClosure<T>(foo: T) { let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") if bar.count > 1 && !(bar.map{ $0 == "()" }.contains(false)) { if let foo = foo as? () -> () { foo() } } } func isASingleDoubleReturnTypeClosure<T>(foo: T) -> Bool { let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") return bar.count > 1 && bar[1] == "Double" /* rhs of '&&' lazily evaluated: [1] ok */ } func printTwoTimesResultOfVoidDoubleClosure<T>(foo: T) { if isAVoidParamClosure(foo) && isASingleDoubleReturnTypeClosure(foo) { if let foo = foo as? () -> Double { let a: Double = 2*foo() print(a) } } } 

Call examples:

 /* Example calls */ let a : () -> () = { print("Foobar") } let b : (Double) -> (Bool) = { $0 > 0 } let c : () -> Double = { 21.0 } let d : Int = 1 isAVoidParamClosure(a) // true isAVoidParamClosure(b) // false isAVoidParamClosure(c) // true isAVoidParamClosure(d) // false callIfVoidVoidClosure(a) // Prints "Foobar" callIfVoidVoidClosure(b) callIfVoidVoidClosure(c) callIfVoidVoidClosure(d) printTwoTimesResultOfVoidDoubleClosure(a) printTwoTimesResultOfVoidDoubleClosure(b) // Prints "42.0" printTwoTimesResultOfVoidDoubleClosure(c) printTwoTimesResultOfVoidDoubleClosure(d) 
+4
source

All Articles