SoFunction
Updated on 2025-04-10

Example of method to replace delegate by RxSwift

Target

Recently I wrote a project and wrote a case where I need to add a rx subscription method to a control I wrote.

Currently there is a proxy:

// Get results using proxy@objc public protocol ZZPhotoPickerControllerDelegate : NSObjectProtocol {
 @objc optional func photoPickerController(_ photoPickerController: ZZPhotoPickerController, didSelect assets: [Any])
}

Need to write an extension that can implement the following method

(onNext: { assets in
 // do something
}

Ideas

At first, I was completely confused. Later I thought of Rx writing an extension to UICollectionViewDelegate:

(onNext: { indexPath in
 // do something
}

It's the same as my needs.

So I looked at the source code of itemSelected:

/// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`.
 public var itemSelected: ControlEvent<IndexPath> {
  let source = (#selector((_:didSelectItemAt:)))
   .map { a in
    return try castOrThrow(, a[1])
   }
  
  return ControlEvent(events: source)
 }

souce is an Observable generated by. What is delegate? Why is there a methodInvoked method? So I continued to click in.

extension Reactive where Base: UIScrollView {
   /// ...This part of the code is omitted and you don't need to read it
  /// Reactive wrapper for `delegate`.
  ///
  /// For more information take a look at `DelegateProxyType` protocol documentation.
  public var delegate: DelegateProxy&lt;UIScrollView, UIScrollViewDelegate&gt; {
   return (for: base)
  }
  
  /// ...The following code doesn't need to be read for the time being}

You can see that delegate is a DelegateProxy<UIScrollView, UIScrollViewDelegate> type, which is a proxy that is literally understood. Then I also saw that rx here is extended from UIScrollView, and UICollectionView is inherited from UIScrollView. You can know that the delegate here is also inherited and used. You can also see the RxScrollViewDelegateProxy thing. You can think that if we want to imitate it, we should also write such a proxy proxy class. Click in first to see:

open class RxScrollViewDelegateProxy
 : DelegateProxy&lt;UIScrollView, UIScrollViewDelegate&gt;
 , DelegateProxyType 
 , UIScrollViewDelegate {

 /// Typed parent object.
 public weak private(set) var scrollView: UIScrollView?

 /// - parameter scrollView: Parent object for delegate proxy.
 public init(scrollView: ParentObject) {
   = scrollView
  (parentObject: scrollView, delegateProxy: )
 }

 // Register known implementations
 public static func registerKnownImplementations() {
   { RxScrollViewDelegateProxy(scrollView: $0) }
   { RxTableViewDelegateProxy(tableView: $0) }
   { RxCollectionViewDelegateProxy(collectionView: $0) }
   { RxTextViewDelegateProxy(textView: $0) }
 }

 /// ...The feeling behind it doesn't matter, I won't read it now}

You can see that it is actually a DelegateProxy<UIScrollView, UIScrollViewDelegate>, and it complies with the DelegateProxyType and UIScrollViewDelegate protocols. You can feel that it is a link between rx and delegate. There is an instance variable scrollView, there is an init method, and there is a registerKnownImplementations static method.

Now there is a vague idea in our mind: we need to first create a link delegateProxy object, then create a delegateProxy instance in the rx extension of the target class, and finally use this delegateProxy methodInvoked in our assetsSelected event stream to intercept the target method in the delegate, and generate a subscribed Observable to return it to the controlEvent, so that the link is opened.

start

First create an RxPhotoPickerControllerDelegateProxy

class RxPhotoPickerControllerDelegateProxy: DelegateProxy&lt;ZZPhotoPickerController, ZZPhotoPickerControllerDelegate&gt;, DelegateProxyType, ZZPhotoPickerControllerDelegate {
 
 /// Typed parent object.
 public weak private(set) var photoPickerController: ZZPhotoPickerController?
 
 /// - parameter scrollView: Parent object for delegate proxy.
 public init(photoPickerController: ParentObject) {
   = photoPickerController
  (parentObject: photoPickerController, delegateProxy: )
 }
 
 static func registerKnownImplementations() {
   { RxPhotoPickerControllerDelegateProxy(photoPickerController: $0) }
 }
 
 // After writing the above, the editor will prompt you to implement two methods, one is to obtain and the other is to set, so it is easy to understand what to implement in the method. static func currentDelegate(for object: ZZPhotoPickerController) -&gt; ZZPhotoPickerControllerDelegate? {
  return 
 }
 
 static func setCurrentDelegate(_ delegate: ZZPhotoPickerControllerDelegate?, to object: ZZPhotoPickerController) {
   = delegate
 }
 
}

Then write a delegateProxy instance to the target rx extension:

extension Reactive where Base: ZZPhotoPickerController {
 
 public var zzDelegate: DelegateProxy<ZZPhotoPickerController, ZZPhotoPickerControllerDelegate> {
  return (for: base)
 }
 
}

Finally, write our assetsSelected:

extension Reactive where Base: ZZPhotoPickerController {
 
 var assetsSelected: ControlEvent<[Any]> {
  let source: Observable<[Any]> = (#selector((_:didSelect:))).map { a in
   return a[1] as! [Any]
  }
  return (events: source)
 }
 
}

Note that there is a method castOrThrow inside. This method rx is not open, it is an internal method. If you write an error according to the write. It can be found that this method is just a type inference, so it can be written simply.

Finish

Then you can happily subscribe to assetsSelected.

(onNext: { (assets) in
    // do something
   }).disposed(by: )

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for your study or work. Thank you for your support.