Creating a proxy of an object and casting it as the object's type

Is it possible to create a proxy of a NSObject and casting it as the object's type.
Specifically I want to create a similar behaviour as NSView's func animator() -> Self which returns the view as proxy. The view returned by func animator() -> Self returns isProxy() = true.

You can unsafeDownCast when you return whatever proxy object you've authored to make it type check, but I think this is probably a really bad idea for most use cases.

As you may know, this works in Objective-C because everything is dynamically dispatched through objc_msgSend and there are hooks that get called when the proxy can't handle the message.

Swift does its best to statically dispatch as many function calls as possible, even for types that inherit from NSObject. So unless you are extremely careful with any code you write in the proxy, you may find yourself hitting this issue. @objcMembers would be helpful in this regard, but I don't know how far you can trust that when it comes to extensions. Swift bans any mention of NSInvocation and NSProxy in objc, probably for the reasons I just listed.

If you want something that looks a lot like an existing type, but does extra stuff, you probably want dynamicMemberLookup.

I feel like there might be something cool you can do with creating a new type at runtime to make this work if you know for sure that every function call or variable access on your type is dynamic, but like I said, I think this is a very high bar to clear.

1 Like

I managed to create a NSProxy subclass in Objective-C that can be casted as its object type.

@interface ObjectProxy : NSProxy
@property (nonatomic, strong) id target;
- (instancetype)initWithTarget:(id)target;
@end

@implementation ObjectProxy

- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [_target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation setTarget:_target];
    [invocation invoke];
}
@end

In Swift I can use:

let myObject = MyObject()
let proxy = ObjectProxy(target: myObject)
let proxiedObject = proxy as? MyObject

Problem: When I use any subclass I created as target, I can't cast the proxy to the subclass type and only to the object type I subclassed:

class CustomView: NSView {  }

let view = CustomView()
let proxy = ObjectProxy(target: view)
proxy as? CustomView // Fails
proxy as? NSView // Works

However casting a proxy of a subclass I create in Objective-C works:

@interface CustomView : NSView
@end

@implementation CustomView
@end

// Swift
let view = CustomView()
let proxy = ObjectProxy(target: view)
proxy as? CustomView // Works

I found this NSProxy dynamic casting to Swift or ObjC class behaves differently, but I'm unsure if it's related.

Thanks. Using unsafeDownCast crashes when used with my ObjectProxy. E.g unsafeDowncast(proxy, to: NSView.self)

Casting the proxy using Objective-C works. But it only allows my the call @objc dynamic properties/methods of a subclass or else it crashes.

@interface NSObject (Proxy)
- (instancetype)objectProxy;
@end

@implementation NSObject (Proxy)
- (instancetype)objectProxy {
    return (id)[[ObjectProxy alloc] initWithTarget:self];
}
@end
class CustomView: NSView {
    @objc dynamic func method1() { }
    func method2() { }
 }

let customView = NSView()
let proxy = customView.objectProxy()
proxy?.method1() // Works
proxy?.method2() // Fails

NSView's animator() lets my use any property/method, regardless if it's @objc dynamic. I'm still wondering how it's done.