Skip to content

Instantly share code, notes, and snippets.

@pbk20191
Last active April 30, 2025 01:10
Show Gist options
  • Save pbk20191/a4aee4e6ed2d643f7a25fbb737837cd3 to your computer and use it in GitHub Desktop.
Save pbk20191/a4aee4e6ed2d643f7a25fbb737837cd3 to your computer and use it in GitHub Desktop.
Objective-c proxy pattern using Pure Swift!
import Foundation
import ObjectiveC
final class SwiftNSProxyObject<T:NSObjectProtocol>: NSProxy {
@nonobjc
var baseObject:T!
typealias MethodLookup = @convention(c) (AnyObject, Selector, Selector) -> (Unmanaged<NSObject>?)
@nonobjc
var methodTable:MethodLookup?
@nonobjc static var methodSelector: Selector {
Selector( "methodSignatureForSelector:")
}
@nonobjc class func create(_ baseObject:T) -> Self {
let proxy = alloc()
proxy.baseObject = baseObject
if let method = class_getInstanceMethod(type(of: baseObject), Self.methodSelector) {
let imp = method_getImplementation(method)
proxy.methodTable = unsafeBitCast(imp, to: MethodLookup.self)
} else {
proxy.methodTable = nil
}
return proxy
}
@objc(forwardingTargetForSelector:)
func forwardingTarget(for aSelector: Selector!) -> Any? {
#if !DEBUG
if baseObject.responds(to: aSelector) {
return baseObject
}
#endif
return nil
}
// @objc(resolveClassMethod:)
// class func resolveClassMethod(_ sel:Selector) -> Bool {
// false
// }
//
// @objc(resolveInstanceMethod:)
// class func resolveInstanceMethod(_ sel:Selector) -> Bool {
// return false
// }
//
// @objc(instancesRespondToSelector:)
// class func instancesRespond(_ sel:Selector) -> Bool {
// false
// }
@objc(conformsToProtocol:)
override func conforms(to aProtocol: Protocol) -> Bool {
return baseObject.conforms(to: aProtocol)
}
@objc(respondsToSelector:)
override func responds(to aSelector: Selector!) -> Bool {
return baseObject.responds(to: aSelector)
}
@available(swift, obsoleted: 1.0)
@objc(forwardInvocation:)
override func forwardInvocation(_ invocation: NSInvocation) {
invocation.invoke(withTarget: baseObject!)
}
@available(swift, obsoleted: 1.0)
@objc(methodSignatureForSelector:)
override func methodSignature(for selector: Selector) -> NSMethodSignature? {
guard let methodTable else { return nil }
let object = methodTable(self.baseObject, Self.methodSelector, selector)?.takeUnretainedValue()
let signature = object as? NSMethodSignature
return signature
}
@objc(isProxy)
override func isProxy() -> Bool {
return true
}
@objc(isEqual:)
override func isEqual(_ object: Any?) -> Bool {
return baseObject.isEqual(object)
}
@objc(hash)
override var hash:Int {
baseObject.hash
}
@objc(isKindOfClass:)
override func isKind(of aClass: AnyClass) -> Bool {
return baseObject.isKind(of: aClass)
}
@objc(isMemberOfClass:)
override func isMember(of aClass: AnyClass) -> Bool {
return baseObject.isMember(of: aClass)
}
}
import Foundation
import ObjectiveC
final class SwiftProxyObject<T:NSObjectProtocol> {
@nonobjc
let baseObject:T
typealias MethodLookup = @convention(c) (AnyObject, Selector, Selector) -> (Unmanaged<NSObject>?)
@nonobjc
let methodTable:MethodLookup?
@nonobjc static var methodSelector: Selector {
Selector( "methodSignatureForSelector:")
}
@nonobjc
init(baseObject: T) {
self.baseObject = baseObject
let type = type(of: baseObject)
if let method = class_getInstanceMethod(type, Self.methodSelector) {
let imp = method_getImplementation(method)
self.methodTable = unsafeBitCast(imp, to: MethodLookup.self)
} else {
self.methodTable = nil
}
}
@objc(forwardingTargetForSelector:)
func forwardingTarget(for aSelector: Selector!) -> Any? {
#if !DEBUG
if baseObject.responds(to: aSelector) {
return baseObject
}
#endif
return nil
}
// @objc(resolveClassMethod:)
// class func resolveClassMethod(_ sel:Selector) -> Bool {
// false
// }
//
// @objc(resolveInstanceMethod:)
// class func resolveInstanceMethod(_ sel:Selector) -> Bool {
// return false
// }
//
// @objc(instancesRespondToSelector:)
// class func instancesRespond(_ sel:Selector) -> Bool {
// false
// }
@objc(conformsToProtocol:)
func conforms(to aProtocol: Protocol) -> Bool {
return baseObject.conforms(to: aProtocol)
}
@objc(respondsToSelector:)
func responds(to aSelector: Selector!) -> Bool {
return baseObject.responds(to: aSelector)
}
@available(swift, obsoleted: 1.0)
@objc(forwardInvocation:)
func forwardInvocation(_ invocation: NSInvocation) {
invocation.invoke(withTarget: baseObject)
}
@available(swift, obsoleted: 1.0)
@objc(methodSignatureForSelector:)
func methodSignature(for selector: Selector!) -> NSMethodSignature! {
guard let methodTable else { return nil }
let object = methodTable(self.baseObject, Self.methodSelector, selector)?.takeUnretainedValue()
let signature = object as? NSMethodSignature
return signature
}
@objc(isProxy)
func isProxy() -> Bool {
return true
}
@objc(isEqual:)
func isEqual(_ object: Any?) -> Bool {
return baseObject.isEqual(object)
}
@objc(hash)
var hash:Int {
baseObject.hash
}
@objc(isKindOfClass:)
func isKind(of aClass: AnyClass) -> Bool {
return baseObject.isKind(of: aClass)
}
@objc(isMemberOfClass:)
func isMember(of aClass: AnyClass) -> Bool {
return baseObject.isMember(of: aClass)
}
@objc(self)
func `self`() -> NSObjectProtocol {
baseObject.`self`()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment