Is there a simple and definite way in Swift to check whether something is a callable block / function? In some languages it's a trivial thing, but perhaps I'm looking at this from a wrong perspective 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, though there is no such data type. I can check by using a solid signature, but there might be a situation where I don't care about the signature* and simply want to invoke it. For example:
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 like this, which will work for Void
and Bool
return types, but doing this for every type is crazy, especially since I don't care about it, but 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 is a sin. For the argument sake let's just not care about the return type.
How to programmatically check if Variable Type is Int in Swift? To check if a variable or object is an Int, use is operator as shown in the following expression. where x is a variable/object. The above expression returns a boolean value: true if the variable is an Int, or false if not an Int.
As with any other type, you can leave it to Swift to infer the function type when you assign a function to a constant or variable: You can use a function type such as (Int, Int) -> Int as a parameter type for another function.
Swift defines a Never type, which indicates that a function or method doesn’t return to its caller. Functions and methods with the Never return type are called nonreturning. Nonreturning functions and methods either cause an irrecoverable error or begin a sequence of work that continues indefinitely.
There are three types of statements in Swift: 1. Simple Statements The simple statement consists of either an expression or declaration. For example, Here, var score = 9 * 5 is a statement that assigns the result of 9 * 5 to the score variable. Simple statements are the most common types of statements in Swift.
You can check the String representation of .dynamicType
of the callable for existence of substring ->
. Not super-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 wouldn't know anything about the parameters of the callable (but perhaps that could be next step to find out, given that you know it's a callable).
Addition with regard to OPs questions below: just a technical discussion, and not recommended techniques.
You use the same approach as above to check if the function argument is a closure without arguments (() -> (...)
) or one with neither arguments nor return type (() -> ()
), and so on. Using this approach, you can define a generic function that call the argument sent to the function only if it is of a certain closure type. For this "in-function-call", you'll have to make use of type conversion to expected closure type, much as you've described in your Q above. It'll probably be difficult to circumvent this "non-generic" approach w.r.t. calling the closures. A few examples follow below.
/* 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)
}
}
}
Example calls:
/* 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With