Reputation: 6694
I'll begin with small code examples, made as simple as possible to sum up the problem.
Let's say I've got a function defined as follow :
func doSomething<T>(using f: (String, (T) -> Void) -> Void, key: String) {
f("test", {
object in
// do something with object using key
//...
})
}
I've got a bunch of auto generated classes with class methods, for exemple :
class MyClass {
var prop1: String = ""
}
class MyClassAPI {
class func f1(param: String, completion: (MyClass) -> Void) {
let mc = MyClass()
mc.prop1 = "hello"
completion(mc)
}
}
Then, I got a whole set of key/functions couples in a dictionary :
// Any because of cast problem !
let keyFunctions: [String: Any] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
Finally, i'll iterate through all the keys/function to call doSomething
:
for (k, f) in keyFunctions {
// This line doesn't work as the compiler complain about typing errors
doSomething(using: f, key: k)
}
The problem I'm facing is that I can't cast my functions to the right type to pass them to doSomething
:
Xcode suppose to force cast using f as! (String, (_) -> ()) -> Void
then gives me an error, _ is not a type.
I tried to be more permissive using if let uf = f as? (String, (Any) -> Void) -> Void
with no chance.
I read the whole generics pages of Swift manual without any hint on how to achieve this.
Please let me know of any existing way to perform such things using genericity.
Upvotes: 1
Views: 336
Reputation: 6694
I finally made it this way :
func doSomething<T>(using f: (String, (Any) -> Void, key: String, type: T.Type) -> Void, key: String) {
f("test", {
object in
guard let o as? type else { return }
// do something with o using key
//...
})
}
let keyFunctions: [String: (String, (Any) -> Void)] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
Upvotes: 0
Reputation: 2887
Instead of Any
make a more specific type for a block
let keyFunctions: [String: (String) -> Any]
Then, at least, your will compile and doSomething
will be called.
However, there is no point in making it generic, as T
will always be Any
. If doSomething
relies on common behaviour between your classes, then it would make sense to define a protocol for all of them.
If you actually want to have information about your classes, then you can introduce a non generic class that would keep this info:
class Wrap {
var resultType: Any.Type
let f: (String) -> Any
init<T>(_ f: @escaping (String) -> T) {
self.f = f
resultType = T.self
}
}
let keyFunctions: [String: Wrap] = [
"first" : Wrap(MyClassAPI.f1),
"second" : Wrap(MySecondClassAPI.f2)
]
But resultType
can not be used in casting later though.
Upvotes: 1