4

As I understand reflection in Swift is poorly available as of yet. I am currently in the process of converting objective-c code to swift for the sake of performance (I have noticed a considerable difference).

Now what I need is a way to call a Method using reflection. The object the method needs to be called upon extends NSObject to enable the class to be resolved using the following code;

let clazz = NSClassFromString("MyProject.DynamicClass") as NSObject.Type; 
let clazzInstance = clazz() as! NSObject;

I am able to retrieve a the number of argument and a reference to the method using the following code;

let selectorMethod = Selector("myCustomMethod:");

let numberOfArguments : UInt32 = method_getNumberOfArguments(selectorMethod);
let referenceToMethod : Method = class_getInstanceMethod(clazz, selector!);

But how do I use/call the referenceToMethod?

Additional
I have also tried calling performSelector but this has been completely removed Swift 2. I also would like to prevent the use of any @objc attributes/annotations.

Thizzer
  • 16,153
  • 28
  • 98
  • 139
  • May I ask why you absolutely need to use reflection? An example of your use case? I believe almost anything can be done statically at compile-time with Swift, and that's what Swift is all about – Kametrixom Jun 22 '15 at 09:24
  • I don't have a code example atm but for example I want to load a module from a 'framework' which can be specified dynamically and called from UI-code without the programmer knowing what modules are available since modules can be added later by other people. – Thizzer Jun 22 '15 at 09:53

2 Answers2

2

If you are looking for a completely Swifty way of reflection, the object that has the method that needs to be called does not need to be a NSObject at all, instead all it need is a required initializer. Have a look at below example :

class A {
    required init() {}

    func printSomething(s: String) {
        print(s)
    }
}

// initializing object dynamically from class
let clazz = NSClassFromString("MyProject.A") as! A.Type   
let clazzInstance = clazz()

// getting and calling its methods in Swifty way    
let method = clazzInstance.printSomething 
method("something")

The advantage of using this stands on fact that you wont need to use casting at all and also calling method with wrong arguments would trigger a compile time error

Pavel Stepanov
  • 891
  • 8
  • 13
Zell B.
  • 10,266
  • 3
  • 40
  • 49
  • 1
    But what if I only have the name of the method during the runtime - I don't know the name of the method in advance. So I have a string-value "printSomething" how do I call it then? – Thizzer Jun 22 '15 at 09:54
  • I'm afraid that there is no such an easy way like performSelector in ObjC for this. I think you should write a switch statement with each method case – Zell B. Jun 22 '15 at 10:08
  • Do you have any idea if a feature like this will appear in swift any time soon? – Thizzer Jun 22 '15 at 10:32
  • Since Swift methods are statically typed I don't know if methods such `performSelector` will ever appear. But again there is a huge advantage using these because you wont deal with missing selector errors and runtime exceptions – Zell B. Jun 22 '15 at 10:41
  • 1
    I accepted yours as the answer, since the answer is currently 'no' – Thizzer Jun 25 '15 at 08:43
1

A crazy idea, and not perfect:

you can define a var as:

var myFunctionToBe : (() -> AnyObject)?

or even set AnyObject as parameter, and then in the initiator:

init() {
    myFunctionToBe = {
       //do something
       return whatsdone
    }
}

then in reflection you can see the var myFunctionToBe, get it as:

let method = c.value as? (() -> AnyObject)

and invoke it:

method!()

or

let returnVal = method!() as? String
RoyBS
  • 1,241
  • 16
  • 16