Search code examples
iosswiftswift4.1

Call a method with dynamic class name in swift


How can we call class functions with a dynamic class name?

Assume the following example where I have two class with methods with same signature

class Foo{
   class func doSomething()

}

class Foobar {
   class func doSomething()
}

class ActualWork{
   //call following method with a variable type so that it accepts dynamic class  name
   func callDynamicClassMethod(x: dynamicClass)
    x.doSomething() 
  }

How can this be implemented so that x accepts values at run time

Edit: Sorry, I missed to mention that I was looking for any other ways other than protocol oriented approach. This is more of an exploratory question to explore if there is a more direct approach/pods/libraries to achieve this.


Solution

  • I liked this question, because it made me to think a lit'bit outside of the box.

    I'll answer it, by dividing it into a few parts.

    First

    call class functions

    Class function is basically a Type methods, which can be achieved using the static word inside the class context.

    Taking that into account, you can get a simple solution, using protocol and passing the class reference (conforming to that protocol) like this:

    protocol Aaa{
       static func doSomething();
    }
    class Foo : Aaa{
      static func doSomething() {
        print("Foo doing something");
      }
    }
    class FooBar : Aaa{
      static func doSomething() {
        print("FooBar doing something");
      }
    }
    
    class ActualWork{
    
      //Using class (static) method
      func callDynamicClassMethod <T: Aaa> (x: T.Type) {
        x.doSomething();
      }
    }
    
    
    //This is how you can use it
    func usage(){
        let aw = ActualWork();
    
        aw.callDynamicClassMethod(x: Foo.self);
        aw.callDynamicClassMethod(x: Foo.self);
    }
    

    Second

    In case you don't really need the method on the class context, you may consider using instance methods. In that case the solution would be even simpler, like this:

    protocol Bbb{
      func doSomething();
    }
    class Bar : Bbb{
      func doSomething() {
        print("Bar instance doing something");
      }
    }
    class BarBar : Bbb{
      func doSomething() {
        print("BarBar instance doing something");
      }
    }
    class ActualWork{
      //Using instance (non-static) method
      func callDynamicInstanceMethod <T: Bbb> (x: T){
        x.doSomething();
      }
    }
    //This is how you can use it
    func usage(){
       let aw = ActualWork();
        aw.callDynamicInstanceMethod(x: Bar());
        aw.callDynamicInstanceMethod(x: BarBar());
    }
    

    Third

    If you need to use the class func syntax, as OP originally did:

    class func doSomething()

    You CANNOT simply use a protocol. Because protocol is not a class... So compiler won't allow it.

    Cannot declare class function inside protocol

    But it's still possible, you can achieve that by using Selector with NSObject.perform method

    like this:

    class ActualWork : NSObject{
    
        func callDynamicClassMethod<T: NSObject>(x: T.Type, methodName: String){
            x.perform(Selector(methodName));
        }
    
    }
    
    class Ccc : NSObject{
        @objc class func doSomething(){
            print("Ccc class Doing something ");
        }
    
    }
    
    class Ddd : NSObject{
        @objc class func doSomething(){
            print("Ccc class Doing something ");
        }
    
        @objc class func doOther(){
            print("Ccc class Doing something ");
        }
    }
    
    //This is how you can use it
    func usage() {
        let aw = ActualWork();
    
        aw.callDynamicClassMethod(x: Ccc.self, methodName: "doSomething");
        aw.callDynamicClassMethod(x: Ddd.self, methodName: "doSomething");
        aw.callDynamicClassMethod(x: Ddd.self, methodName: "doOther");
    
    }