Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can @dynamicMemberLookup be used to call methods?

Tags:

dynamic

swift

In the documentation for @dynamicMemberLookup it says,

Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime.

If I'm not mistaken, instance methods are considered members of a struct / class. However, when I try to call a function dynamically I get an error saying:

Dynamic key path member lookup cannot refer to instance method foo()

To reproduce the problem:

struct Person {
    var name: String
    var age: Int
    
    func greet() {
        print("hello, my name is \(name)")
    }
}

@dynamicMemberLookup
struct Wrapper {
    var value: Person
    
    subscript<T>(dynamicMember keypath: KeyPath<Person, T>) -> T {
        value[keyPath: keypath]
    }
}

let person = Person(name: "John Doe", age: 21)
let wrapper = Wrapper(value: person)

wrapper.greet()  // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
// Or
let function = wrapper.greet  // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
function()

How can I dynamically call greet() using @dynamicMemberLookup? Is there any way to achieve what I'm trying to do?

Thanks in advance!

like image 939
rayaantaneja Avatar asked Sep 07 '25 18:09

rayaantaneja


1 Answers

No, dynamicMemberLookup does not work for methods. As the signature of the subscript suggests, it only works for things that can be represented as a KeyPath. Method calls cannot be part of a key path. :(

Key-Path Expression

A key-path expression refers to a property or subscript of a type.

The path consists of property names, subscripts, optional-chaining expressions, and forced unwrapping expressions. Each of these key-path components can be repeated as many times as needed, in any order.

At compile time, a key-path expression is replaced by an instance of the KeyPath class.

I suspect the reason why it is called "dynamic member lookup" is because it also works with subscripts. The alternative of dynamicPropertyOrSubscriptLookup is rather a mouthful isn't it?

One rather hacky fix would be to change greet into a computed property:

var greet: () -> Void { {
    print("hello, my name is \(name)")
} }

If greet has had parameters, you could also change it into a subscript, but I think that is an even uglier solution.

like image 162
Sweeper Avatar answered Sep 10 '25 13:09

Sweeper