DelegateProxy is a core feature in Rxswift to wrap the return value from original delegate to observable sequence. So it's important to discover how it works in RxSwift. Under the hood, message forwarding, a feature in Objective-C, plays a role in forwarding message from proxy to real delegate. You should have some basic knowledges about Objective-C Runtime before you go deeper. You can learn that in this post (Chinese version): Objective-C Runtime详解.

Use Cases

There are two ways to apply DelegateProxy to your app in different scenarios:

  • Delegate method has a return value.
  • Delegate method hasn't a return value, in other words, the type of method is void.

Here are some reasons about why we need differential measure to the two cases above, which I quoted from RxSwift Reactive Programming with Swift:

  • Delegate methods with a return type are not meant for observation, but for customization of the behavior.
  • Defining an automatic default value which would work in any case is a non-trivial task.

HAS NOT a Return Value

We use sentMessage(Selector) or methodInvoked(Selector) to establish a relationship between a selector which represents the method in Delegate you want to invoke and a observable sequence. The difference between those methods is whether elements are sent before message forwarding or not. More details about that will be introduced in the later.

var didUpdateLocations: Observable<[CLLocation]> { 
	return delegate
		.methodInvoked(#selector(CLLocationManagerDelegate.locationManager(_:didUpdateLocations:)))
		.map { parameters in
			return parameters[1] as! [CLLocation]
		} 
}

HAS a Return Value

As we discussed before, a method that has a return value is not easy to be wrapped to observable, therefore we need to forward the invocation to real delegate. Suppose you want to MapViewDelegate work with RxSwift, you have a file named MKMapView+Rx.swift for making MapViewDelegate reactive and a file named ViewController.swift which conforms to MKMapViewDelegate as usual.

// MKMapView+Rx.swift

// install a forwarding delegate
func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable {
    return RxMKMapViewDelegateProxy.installForwardDelegate(
    	delegate, 
    	retainDelegate: false, 
    	onProxyForObject: self.base
    )
 }

// manipulate how map view displays when new data have been arrived
var overlays: Binder<[MKOverlay]> {
    return Binder(self.base) { (mapView: Base, overLays: [MKOverlay]) in
      mapView.removeOverlays(mapView.overlays)
      mapView.addOverlays(overLays)
    }
  }

Then, you can call this function to set up with the real delegate instance.

// ViewController.swift

// register forwarding delegate
mapView.rx.setDelegate(self).disposed(by: bag)

// regular protocol implementation
extension ViewController: MKMapViewDelegate {
  public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    guard let overlay = overlay as? ApiController.Weather.Overlay else {
      return MKOverlayRenderer()
    }
    let overlayView = ApiController.Weather.OverlayView(overlay: overlay, overlayIcon: overlay.icon)
    return overlayView
  }
}

You may confuse after viewing this code. Don't worry! You can understand them better after you learned how forwarding delegate works.

Registering a Proxy

According to the source code, we can find all methods related to register is in DelegateProxyType.swift. Let me show you how we register a proxy in an extension of ParentObject first.

We can find those methods in source code.

Protocol: DelegateProxyType

// abstract static method
static func registerKnownImplementations()

// `DelegateProxy.identifer` is, in fact, an identifier of Delegate.
public static var identifier: UnsafeRawPointer {
    let delegateIdentifier = ObjectIdentifier(Delegate.self)
    let integerIdentifier = Int(bitPattern: delegateIdentifier)
    return UnsafeRawPointer(bitPattern: integerIdentifier)!
}

// static method
public static func register<Parent>(make: @escaping (Parent) -> Self) {
    self.factory.extend(make: make)
}

// self.factory
private static var factory: DelegateProxyFactory {
    DelegateProxyFactory.sharedFactory(for: self)
}

Class: DelegateProxyFactory

private class DelegateProxyFactory {
  private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:]

    fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
        MainScheduler.ensureRunningOnMainThread()
        let identifier = DelegateProxy.identifier
        if let factory = _sharedFactories[identifier] {
            return factory
        }
        let factory = DelegateProxyFactory(for: proxyType)
        _sharedFactories[identifier] = factory
        DelegateProxy.registerKnownImplementations()
        return factory
    }

    private var _factories: [ObjectIdentifier: (AnyObject) -> AnyObject]
    private var _identifier: UnsafeRawPointer
  
    // ...

    fileprivate func extend<DelegateProxy: DelegateProxyType, ParentObject>(make: @escaping (ParentObject) -> DelegateProxy) {
        MainScheduler.ensureRunningOnMainThread()
        precondition(_identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier")
        guard _factories[ObjectIdentifier(ParentObject.self)] == nil else {
            rxFatalError("The factory of \(ParentObject.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
        }
        _factories[ObjectIdentifier(ParentObject.self)] = { make(castOrFatalError($0)) }
    }
  
  // ...
}

Notes

  • Mapping relations between Delegate and an instance of DelegateProxyFactory are saved in _sharedFactories, and mapping relations between ParentObject and Proxy for each Delegate are saved in _factories, an instance variable in DelegateProxyFactory.

  • Method saving the relations mentioned above is sharedFactory(for: DelegateProxy.Type) -> DelegateProxyFactory and extend(make: @escaping (ParentObject) -> DelegateProxy). It's easy to understand what happens if you neglect the code for log.

You override registerKnownImplementations() in the root of proxy and register all proxies corresponding to views that are inherited from root, since createProxy(_:) in the factory creates proxy is based on what type of object you pass is and root proxy shares the same variable, e.g. xxx.rx.delegate, with their children.

For example, we know that the hierarchical relation in UITableView is: UITableView > UIScrollView > UIView.

// RxScrollViewDelegateProxy.swift
public static func registerKnownImplementations() {
    self.register { RxScrollViewDelegateProxy(scrollView: $0) }
    self.register { RxTableViewDelegateProxy(tableView: $0) }
    self.register { RxCollectionViewDelegateProxy(collectionView: $0) }
    self.register { RxTextViewDelegateProxy(textView: $0) }
}

In this case, UIScrollView is root, of course, its corresponding proxy, RxScrollViewDelegateProxy, is root as well. Therefore, in registerKnownImplementations() it registers all proxies of children, including RxTableViewDelegateProxy. And now you glance through the source code of UIScrollView+Rx.swift and UITableView+Rx.swift.

// UIScrollView+Rx.swift
public var delegate: DelegateProxy<UIScrollView, UIScrollViewDelegate> {
    return RxScrollViewDelegateProxy.proxy(for: base)
}

Although there is no definition about delegate in UITableView+Rx.swift, in some methods they use self.delegate to call methods in Delegate.

// UITableView+Rx.swift
public var itemSelected: ControlEvent<IndexPath> {
    let source = delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didSelectRowAt:)))
        .map { a in
            try castOrThrow(IndexPath.self, a[1])
        }
    return ControlEvent(events: source)
}

The reason is that RxScrollViewDelegateProxy.proxy(for:) calls createProxy(_:) finally. createProxy(_:), however, creates proxy depends on what type of object you passed in. For example, you call delegate in UITableView+Rx.swift, base passing to RxScrollViewDelegateProxy.proxy is an instance of UITableView, then the return type of proxy from createProxy(_:) is RxTableViewDelegateProxy.

Here is a flowchart to show you the process about registration.

st=>start: Start
e=>end: End
initFormat=>operation: init self.factory
performShareFactory=>operation: sharedFactory(for:)
hasFactoryInstance=>condition: has factory 
instance?
returnFactory=>operation: return factory
regKnownImp=>operation: registerKnownImplementation()
register=>operation: register()
extend=>operation: extend()
saveRelation=>operation: save mapper to factory
(parentObj > proxy) 


st->initFormat(right)->performShareFactory
performShareFactory->hasFactoryInstance
hasFactoryInstance(yes)->returnFactory
hasFactoryInstance(no)->regKnownImp(right)->register(right)->extend->saveRelation
saveRelation(left)->returnFactory->e

Creating a Proxy

As usual, we extend a class to create a proxy and use it as Delegate. To create a proxy, we perform proxy(object: ParentObject) to get an instance of DelegateProxy from the factory of DelegateProxy.

public extension Reactive where Base: CLLocationManager {
    var delegate: DelegateProxy<CLLocationManager, CLLocationManagerDelegate> {
        return RxCLLocationManagerDelegateProxy.proxy(for: base)
    }
}

Although creating a proxy could be done in less than three lines of code, RxSwift really does a lot for implementing those functions. The following codes scattered all over the place show you all related stuffs to this process.

Protocol: DelegateProxyType.swift

extension DelegateProxyType {
    // ...
    public static func proxy(for object: ParentObject) -> Self {
        MainScheduler.ensureRunningOnMainThread()

        let maybeProxy = assignedProxy(for: object)

        let proxy: AnyObject
        if let existingProxy = maybeProxy {
            proxy = existingProxy
        } else {
            proxy = castOrFatalError(createProxy(for: object))
            assignProxy(proxy, toObject: object)
            assert(assignedProxy(for: object) === proxy)
        }
        let currentDelegate = _currentDelegate(for: object)
        let delegateProxy: Self = castOrFatalError(proxy)

        if currentDelegate !== delegateProxy {
          	// save the forwarded delegate in `__forwardToDelegate` defined 
          	// in `_RXDelegateProxy.m`
            delegateProxy._setForwardToDelegate(currentDelegate, retainDelegate: false)
            assert(delegateProxy._forwardToDelegate() === currentDelegate)
            // set current proxy as delegate of object 
            _setCurrentDelegate(proxy, to: object)
            assert(_currentDelegate(for: object) === proxy)
            assert(delegateProxy._forwardToDelegate() === currentDelegate)
        }

        return delegateProxy
    }
  	// ...
}

extension DelegateProxyType {
    static func _currentDelegate(for object: ParentObject) -> AnyObject? {
        currentDelegate(for: object).map { $0 as AnyObject }
    }
    
    static func _setCurrentDelegate(_ delegate: AnyObject?, to object: ParentObject) {
        setCurrentDelegate(castOptionalOrFatalError(delegate), to: object)
    }
    
    func _forwardToDelegate() -> AnyObject? {
        self.forwardToDelegate().map { $0 as AnyObject }
    }
    
    func _setForwardToDelegate(_ forwardToDelegate: AnyObject?, retainDelegate: Bool) {
      	// setForwardToDelegate is defined in DelegateProxy.swift
        self.setForwardToDelegate(castOptionalOrFatalError(forwardToDelegate), retainDelegate: retainDelegate)
    }
}

extension DelegateProxyType where ParentObject: HasDelegate, Self.Delegate == ParentObject.Delegate {
    public static func currentDelegate(for object: ParentObject) -> Delegate? {
        object.delegate
    }
    public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
        object.delegate = delegate
    }
}

extension DelegateProxyType {
 	 // ...
    public static func createProxy(for object: AnyObject) -> Self {
        castOrFatalError(factory.createProxy(for: object))
    }
  	// ...
}

private class DelegateProxyFactory {
  	// ...
    fileprivate func createProxy(for object: AnyObject) -> AnyObject {
        MainScheduler.ensureRunningOnMainThread()
        var maybeMirror: Mirror? = Mirror(reflecting: object)
        while let mirror = maybeMirror {
            if let factory = _factories[ObjectIdentifier(mirror.subjectType)] {
                return factory(object)
            }
            maybeMirror = mirror.superclassMirror
        }
        rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.")
    }
}

Class: DelegateProxy.swift

open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
  	// ...
  	private var _sentMessageForSelector = [Selector: MessageDispatcher]()
    private var _methodInvokedForSelector = [Selector: MessageDispatcher]()
  
    open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
        #if DEBUG // 4.0 all configurations
            MainScheduler.ensureRunningOnMainThread()
        #endif
      	// _setForwardToDelegate is defined in _RXDelegateProxy.m
        _setForwardToDelegate(delegate, retainDelegate: retainDelegate)

        let sentSelectors: [Selector] = _sentMessageForSelector.values.filter { $0.hasObservers }.map { $0.selector }
        let invokedSelectors: [Selector] = _methodInvokedForSelector.values.filter { $0.hasObservers }.map { $0.selector }
        let allUsedSelectors = sentSelectors + invokedSelectors

        for selector in Set(allUsedSelectors) {
            checkSelectorIsObservable(selector)
        }
        reset()
    }
  
  	fileprivate func reset() {
        guard let parentObject = _parentObject else { return }

      	// definiation in init(): self._currentDelegateFor = delegateProxy._currentDelegate
        let maybeCurrentDelegate = _currentDelegateFor(parentObject)

        if maybeCurrentDelegate === self {
          	// definiation in init(): self._setCurrentDelegateTo = delegateProxy._setCurrentDelegate
            _setCurrentDelegateTo(nil, parentObject)
            _setCurrentDelegateTo(castOrFatalError(self), parentObject)
        }
    }
  	// ...
}

Class: _RXDelegateProxy.m

id __weak __forwardToDelegate;

-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
    __forwardToDelegate = forwardToDelegate;
    if (retainDelegate) {
        self.strongForwardDelegate = forwardToDelegate;
    }
    else {
        self.strongForwardDelegate = nil;
    }
}

Notes:

  • checkSelectorIsObservable(selector:) does nothing, but just outputs some warning message.

The flowchart of proxy creation is

st=>start: call RXxxxDelegateProxy.proxy(for:)
e=>end: Return Proxy
mayProxy=>condition: mayProxy?
proxy=>inputoutput: proxy
callCreateProxy=>operation: call createProxy(for:)
get proxy from factory
CDChanged=>condition: CurrentDelegate 
changed?
call_setForwardToDelegate=>operation: call _setForwardToDelegate(_:retainDelegate:)
to save forwarded delegate
call_setCurrentDelegate=>operation: call _setCurrentDelegate(_:to:)
to set proxy as delegate of object

st(right)->mayProxy
mayProxy(yes, bottom)->proxy
mayProxy(no, right)->callCreateProxy->proxy
proxy->CDChanged
CDChanged(no, bottom)->e
CDChanged(yes, right)->call_setForwardToDelegate->call_setCurrentDelegate(left)->e

Wrapping to Observable

Class MessageDispatcher has a dispatcher, an instance of PublishSubject, and result, an observable of dispatcher, which you can subscribe it to get events of delegate.

private final class MessageDispatcher {
    private let dispatcher: PublishSubject<[Any]>
    private let result: Observable<[Any]>
  	// ...
    init<P, D>(selector: Selector, delegateProxy _delegateProxy: DelegateProxy<P, D>) {
        // ...
        result = dispatcher
            .do(onSubscribed: { weakDelegateProxy?.checkSelectorIsObservable(selector); weakDelegateProxy?.reset() }, onDispose: { weakDelegateProxy?.reset() })
            .share()
            .subscribeOn(mainScheduler)
    }
    
    // ...
}

DelegateProxy has _sentMessageForSelector and _methodInvokedForSelector to map a selector corresponding to a method of delegate to an instance of MessageDispatcher.

private var _sentMessageForSelector = [Selector: MessageDispatcher]()
private var _methodInvokedForSelector = [Selector: MessageDispatcher]()

You now can emit an event by _sentMessage(_:withArguments:) and _methodInvoked(_:withArguments:).

open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) {
    _sentMessageForSelector[selector]?.on(.next(arguments))
}

open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any]) {
    _methodInvokedForSelector[selector]?.on(.next(arguments))
}

methodInvoked(_:) and sentMessage(_:) saves the mapper into the dictionary and returns result, an member variable in MessageDispather we discussed above.

open func sentMessage(_ selector: Selector) -> Observable<[Any]> {
    MainScheduler.ensureRunningOnMainThread()

    let subject = _sentMessageForSelector[selector]

    if let subject = subject {
        return subject.asObservable()
    } else {
        let subject = MessageDispatcher(selector: selector, delegateProxy: self)
        _sentMessageForSelector[selector] = subject
        return subject.asObservable()
    }
}

open func methodInvoked(_ selector: Selector) -> Observable<[Any]> {
    MainScheduler.ensureRunningOnMainThread()

    let subject = _methodInvokedForSelector[selector]

    if let subject = subject {
        return subject.asObservable()
    } else {
        let subject = MessageDispatcher(selector: selector, delegateProxy: self)
        _methodInvokedForSelector[selector] = subject
        return subject.asObservable()
    }
}

DelegateProxy inherits from _RXDelegateProxy, which contains codes about message forwarding to ensure the message could be forwarded to original delegate, written by Objective-C. When someone calls a function of original delegate on proxy forwardInvocation: will be performed, because proxy can't respond to the selector.

-(void)forwardInvocation:(NSInvocation *)anInvocation {
    BOOL isVoid = RX_is_method_signature_void(anInvocation.methodSignature);
    NSArray *arguments = nil;

    if (isVoid) {
        arguments = RX_extract_arguments(anInvocation);
        [self _sentMessage:anInvocation.selector withArguments:arguments];
    }

    if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self._forwardToDelegate];
    }

    if (isVoid) {
        [self _methodInvoked:anInvocation.selector withArguments:arguments];
    }
}

Notes:

  • _sentMessage:withArguments: and _methodInvoked:withArguments: will be invoked if the return type of method is void.
  • _sentMessage:withArguments: is performed before the message forwards to delegate, and _methodInvoked:withArguments: after that.

Why void methods are differentiated from others? Comments from RXSwift give answer:

Only methods that have void return value can be observed using this method because those methods are used as a notification mechanism. It doesn't matter if they are optional or not. Observing is performed by installing a hidden associated PublishSubject that is used to dispatch messages to observers.

Delegate methods that have non void return value can't be observed directly using this method because:

  • those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
  • there is no sensible automatic way to determine a default return value

You can overwrite the method of delegate to let proxy response to, here is an example:

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if let subject = _contentOffsetBehaviorSubject {
        subject.on(.next(scrollView.contentOffset))
    }
    if let subject = _contentOffsetPublishSubject {
        subject.on(.next(()))
    }
    // DO NOT forget to forward to delegate
    self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
}

Conclusion

Discovering how RxSwift works is an nice exprience for me. I learned how to wrap a regular method to observable stream, and many underlying knowledges about iOS and programming language, such as Objective-C Runtime. Source code, in fact, is not too difficult to read if you determined to do it. You will understand what mechanisms are behind Apis and work with them more elegantly once mastered each part of source code.