1

What I want to accomplish is to pass the dynamic type of object as a parameter to generic function. I'm able to see the correct type I want with type(of:), but I'm unable to pass it as parameter, because in generic function I'm still getting the static type. Here is an example what I want to do:

protocol SomeUsefulProtocol {}

struct MyType1: SomeUsefulProtocol {}
struct MyType2: SomeUsefulProtocol {}

let objects: [SomeUsefulProtocol] = [MyType1(), MyType2()]

let someClosureToWorkWithMyType: Any = { (object: MyType1) in
    //Do some great things
}

func someMethod<T>(type: T.Type, object: Any) {
    typealias ClosureType = (T) -> Void

    if let closure = someClosureToWorkWithMyType as? ClosureType, let object = object as? T {
        closure(object)
    }
}

for object in objects {
    someMethod(type: type(of: object), object: object)
}

Here i want closure someClosureToWorkWithMyType to be called when object has a type 'MyType1', but inside the method the type of object is the static type (SomeUsefulProtocol).

Ruben
  • 290
  • 3
  • 14
  • Compare https://stackoverflow.com/q/45234233/2976878 – Hamish Mar 03 '18 at 11:55
  • Your code same seems a little bit more complicated then it needs. Why are you declaring the closure as `Any`? Secondly, you can simply iterate the array and execute the closure for the objects that are `MyType1`, no need for an intermediary function. – Cristik Mar 04 '18 at 06:42

1 Answers1

0

The problem here is that object as declared as an instance of the protocol SomeUsefulProtocol. An instance of a protocol is not any specific concrete type, and can be one of potentially many concrete types.

Swift's generics system requires known concrete types at compile time to create the right specializations. There are a number of blog posts and StackOverflow questions and answers which delve deeper into the trickiness (and more often than not, the futility)of trying to use Swift protocols and Swift generics together.

In your specific example, your method can be made to work in one of two ways: either don't declare object as a protocol instance, but instead make it a concrete type, e.g.

let object: MyType = MyType()

Or keep it declared as-is, but cast it to a concrete type when passing it into your function, e.g.

(object as? MyType).flatMap{ someMethod(type: type(of: $0), object: $0) }
Daniel Hall
  • 13,457
  • 4
  • 41
  • 37
  • I have edited the question. The solution will not work, because the time I'm using the object, the exact type(dynamic type) is unknown to cast it. That's the reason I'm using generics and type casting. My only issue is to pass the dynamic type of object to method. – Ruben Mar 03 '18 at 11:47
  • @Ruben That's the key problem: Swift generics require that the _exact_ concrete type of any generic parameters or participants be known _at compile time_. This is because the compiler needs to emit the specialized types/function implementations and know how to allocate the memory accordingly. That's why they don't work well with protocols, which effectively erase concrete type and make them polymorphic. So the short answer is: there is no way to get Swift generics to work with parameters that don't have explicit concrete types that are known at compile-time. – Daniel Hall Mar 05 '18 at 17:24