Can I define fallback behavior for the “object has no member" error?

class A {
    func foo() {
        print("foo!")
    }
}

class B {
    let a = A()
}

let b = B()
b.foo()

Naturally, the result of this code is error: value of type 'B' has no member 'foo'.

Is there any way for class B to automatically dispatch any missing methods to B.a? So in this case, b.foo() would be treated as b.a.foo().

(Yes, I’m aware that if I want B to share a calling interface with A, I can 1) refactor so that B is a subclass of A, or 2) conform B and A to a protocol that contains the calling interface. I’m specifically interested in whether the dispatch can be adjusted in the manner suggested.)

(The solution should not require explicit handling of individual methods, otherwise it's basically equivalent to the protocol approach.)

Maybe not what you want, since you have to add each member you want to forward to a explicitly, and for each extra type you want to support you need to add an extra subscript:

class A {
    func foo() {
        print("foo!")
    }
}

@dynamicMemberLookup
class B {
    let a = A()
    
    subscript(dynamicMember dynamicMember: String) -> () -> Void {
        switch dynamicMember {
        case "foo": return a.foo
        default: fatalError()
        }
    }
}

let b = B()
b.foo() // prints "foo!"

Thank you. I have clarified the question to rule out this possibility. I am trying to achieve what we might call “automatic failover” behavior.

Your suggestion seems like it might work if dynamicMember could become a key path, but apparently key paths can’t refer to instance methods.

Analogy: in Racket there's a special identifier called %top that the compiler automatically wraps around top-level identifiers. If the identifier doesn't have a binding, the default %top will produce a runtime error that stops evaluation. Or you can redefine %top to take some other action that you like better. IOW, though %top is not a tool of first resort, it's a hook that lets you interpose custom runtime behavior.

Maybe there isn’t something equivalent in Swift. But we could imagine that when a class B is missing method foo, a method is called like B.methodMissing(\.foo). The default methodMissing from the superclass would raise an error. But we could also override methodMissing within B to do something different.

Unfortunately, there's currently no equivalent feature in Swift — the closest you're likely to get syntactically is using @dynamicMemberLookup, but as you note, keypaths can't refer to instance methods. In general, this would be tricky, too, because even if this were possible, Swift's design principles would likely have conflicts between methods on B and B.a error instead of preferring those on B. Method calls like this generally dispatch statically, so there's no hook like %top you could use.

All of this would be pretty trivial to do in Objective-C (which is dynamic, and has similar types of hooks you could use in B), but sadly, not in Swift.


If you're open to out-of-language solutions, you might be able to use a tool like Sourcery; it could allow you to generate an extension with all of the methods from A which B doesn't have, and add those to B with forwards to B.a.