Search code examples
swiftgenericsswift2metatype

Implementing a registerClass like behavior in Swift using generics


I have a tricky question to figure out. But I'm not able to find out a specific solution for it.

Before explaining the goal this is the code I created so far.

protocol SomeProtocol {

    init()
    func parse()

}

class MyService<T : SomeProtocol> {

    var clazz : T.Type?

    func registerClass(clazz: T.Type) {
        self.clazz = clazz
    }

    func doWork() {

        if let clazz = self.clazz {

            let clazzInstance = clazz.init()
            clazzInstance.parse()

        }
    }

}

class MyClass : SomeProtocol {

    required init() { }

    func parse() {
        print("My Class parsing")
    }

}

class MyClass2 : SomeProtocol {

    required init() { }

    func parse() {
        print("My Class2 parsing")
    }

}

The above code can be used like the following:

let myService = MyService<MyClass>()
myService.registerClass(MyClass.self)
myService.doWork()

let myService2 = MyService<MyClass2>()
myService2.registerClass(MyClass2.self)
myService2.doWork() 

So, in order to explain a little bit about the context. I have a service, MyService in this case. I can create an instance of that service in order to perform some work. The service does a simple job: it receives some parameters and returns some JSON data. Based on the class I registered in the service (see registerClass method), the service will create an instance of that class and then it will invoke some method on it (in fact the class is registered conforms to SomeProtocol protocol). Here it is important to note that the code responsible to create the service class can choose to register or not a class. The clazz property is optional, as you can see.

At this point, this is what I would like to achieve: removing the T at the class level and moving it at the registerClass level. In other words, like the following:

class MyService {

    var clazz : T.Type?

    func registerClass<T : SomeProtocol>(clazz: T.Type) {
        self.clazz = clazz
    }

    // the other code here

}

Based on this assumption, the usage should become:

let myService = MyService()
// I can choose to call this method in order to register a class into it
myService.registerClass(MyClass.self) 
myService.doWork()

Obviously if I do in this manner, this is the error I receive for the class property within MyService class.

Use of undeclared type 'T'

Any suggestions to reach my goal? Thanks.

P.S. Feel free to modify the title. I haven't found a better one.


Solution

  • If all you want is for clazz to conform to a protocol then there is no need for generics. This might do what you want:

    protocol SomeProtocol {
      init()
      func parse()
    }
    
    class MyService {
      var clazz : SomeProtocol?
    
      func registerClass(clazz: SomeProtocol) {
        self.clazz = clazz
      }
    
      func doWork() {
        clazz?.parse()
      }
    }
    
    class MyClass : SomeProtocol {
      required init() { }
    
      func parse() {
        print("My Class parsing")
      }
    }
    
    class MyClass2 : SomeProtocol {
      required init() { }
    
      func parse() {
        print("My Class2 parsing")
      }
    }
    
    let myService = MyService()
    myService.registerClass(MyClass())
    myService.doWork()
    
    let myService2 = MyService()
    myService2.registerClass(MyClass2())
    myService2.doWork()