1

Issue

I want to create a generic method in a specific class, where I can make a call to another class which based on the target (which conforms to specific protocol), can do certain calculations.

If I explicitly define what kind of target it is, it works okay, but if I want to pass it as type which conforms to a specific protocol, I run into trouble.

Not clear don't read

I have a protocol and a class which conforms to it as below:

protocol MyProtocol {
    func someFunc()
}

class ConformingClass: MyProtocol {
    func someFunc() {
        // Do something
    }
}

There is a class that I need to use:

class SomeClass<N: MyProtocol> {
    class func doSomething() {
        // Do something
    }
}

And there is my class where I want to use the SomeClass but the type of it will be defined as a parameter on a method below:

class MyClass {
    func doStuff<C: MyProtocol>(target: C.Type) {
        SomeClass<target>.doSomething
    }
}

Problem

Compilation error: 'target' is not a type

enter image description here

Question

How is that error possible, when I am explicitly saying that the parameter is a Type which conforms to MyProtocol? What am I doing wrong in here?

I am using Swift 2.3 and Xcode 7.3.1

Community
  • 1
  • 1
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • Short version: you can't. Swift doesn't currently support it. You need to add whatever aspects of the class you need into a protocol, or you need to redesign it. There's work in Swift 3 to make this more powerful. – Rob Napier Jul 08 '16 at 13:52
  • @RobNapier This could be correctly answered by what you said, but I am wondering how is this a duplicate? I am not asking how to put a variable conforming to a protocol, I am talking about a Type which is not recognized. – E-Riddie Jul 08 '16 at 13:55
  • 2
    Agreed, this is actually a different issue, and not something that is likely to be addressed any time soon. This is a piece of a more general feature called higher-kinded types that Swift does not have and does not currently plan to add (though it's been discussed extensively). You should rethink your approach, particularly removing classes from the equation and using type-erasure or passed closures instead. – Rob Napier Jul 08 '16 at 14:00
  • @RobNapier thanks for the comment :) Actually this approach that I was trying to do was forced by a library that I use for Object Mapping, and I wanted to make it more generic, but it's a pain that I can't, maybe I will try some other approaches. – E-Riddie Jul 09 '16 at 14:48

2 Answers2

4

In your code target contains a "runtime type object". And you cannot use such thing as a generic type argument. You know you cannot do this:

func doStuff<C: MyProtocol>(target: C.Type) {
    var c: target? //<- This is illegal. You cannot use `target` where a "type" is needed.
}

Why don't you write it as:

func doStuff<C: MyProtocol>(target: C.Type) {
    SomeClass<C>.doSomething()
}

You can use it as:

myObj.doStuff(ConformingClass.self)
OOPer
  • 47,149
  • 6
  • 107
  • 142
  • Thank you for your answer, it is a really nice solution. Code now is compiling, I will test the functionality, which I think it will work too :) – E-Riddie Jul 09 '16 at 15:16
0

Modern approach:

func doStuff(target: any MyProtocol.Type) {
    target.doSomething()
}

You can also use a Collection (Array, Set, etc.) with any to mix-match conforming types:

func doStuff(targets: any Collection<any MyProtocol.Type>) {
    targets.forEach { target in
        target.doSomething()
    }
}

So let's say you have classes A and B which both conform to MyProtocol, these could be passed in the same collection, like:

doStuff(targets: [A.self, B.self])
Ben Guild
  • 4,881
  • 7
  • 34
  • 60