Swift Generics with Protocol.Type

Question

I have a problem with generics in Swift (3):

I get different data of different classes, implementing the same protocol, from server and I need to put them into a class with generics (e.g. Array).

I do not know which class the data will be so I need to use the protocol. So I have following structure:

My protocol:

protocol MyProtocol {
    // some protocol stuff
}

Some classes implementing the protocol

class MyProtocolImpl1: MyProtocol{
    // some class stuff
}

class MyProtocolImpl2: MyProtocol {
    // some class stuff
}
....

class with generic:

final class MyGenericsClass<T: MyProtocol> {
    // some class stuff
}

now I want to use this class this way:

func createClass<T>(model: T.Type) -> MyGenericClass<T> {
    let myClass = MyGenericClass<T>()
    return myClass
}

...

EDIT

func getClass() -> MyProtocol.Type {
     return MyProtocolImpl1.self
}

let impl1 = getClass()
let impl2 = MyProtocolImpl2.self

let createdClass = createClass(impl1) //not working
let createdClass = createClass(impl2) //working

doing createClass(impl1) I get this error:

cannot invoke 'createClass' with an argument list of type '(MyProtocol.Type)'

Changing the MyProtocol to a class would fix the problem but then I could not be sure every class inheriting from it implements the needed methods.

Does someone have some ideas how to solve this problem?


Show source
| generics   | swift   | protocols   2017-01-05 16:01 2 Answers

Answers ( 2 )

  1. 2017-01-05 17:01

    I did not exactly understand what you are trying to do, but I thing this is what you want.

    import Foundation
    
    
    protocol MyProtocol {
    }
    
    class ClassA : MyProtocol {
    }
    
    class ClassB : MyProtocol {
    }
    
    final class GenericClass<T:MyProtocol> {
    
    }
    
    func createClass<T:MyProtocol>(_ model: T.Type) -> GenericClass<T> {
        let myClass = GenericClass<T>()
        return myClass
    }
    let createdClass = createClass(ClassA.self)
    
  2. 2017-01-06 10:01

    The answer is that you can't do this. MyProtocol.self is not a concrete type, ergo it cannot conform to MyProtocol. You cannot pass MyProtocol when your function expects a concrete type T, following T: MyProtocol.

    Think about it this way; if I were to add a static function, doFoo() to MyProtocol, and you initialized GenericClass<MyProtocol> and decided to call MyProtocol.doFoo() inside some method, what function would it call? It doesn't make any sense, because MyProtocol is not a concrete type (i.e. a class, struct or enum), it's a protocol.


    A note on your comment:

    The problem is that I do not know if it is ClassA or ClassB or ClassX. I just know that it is a subclass of MyProtocol

    This is where you are erring. "subclass of MyProtocol" does not make sense, because MyProtocol is not a class.


    How about this? You end up with AnyObject, but you can cast it to MyGenericClass<T> later when you do know T.

    extension MyProtocol
    {
        static func createMyGenericClass() -> AnyObject
        {
            return MyGenericClass<Self>()
        }
    }
    
    func createClass(_ modelType: MyProtocol.Type) -> AnyObject
    {
        return modelType.createMyGenericClass()
    }
    

    You could also make MyGenericClass inherit from some base class, and have that store a variable for MyProtocol.Type. That way, you could use specific functions from your MyProtocol conformee type.

◀ Go back