3

in the last two years I build app with MVVM pattern, and every tutorial always says that MVVM use Reactive library, example: RXSwift or ReactiveCocoa because I am iOS programmer. I just realize

  1. Why we need use Reactive, why we dont use just delegate ?
  2. What delegate cant do over Reactive ?
  3. Pros and Cons each pattern ?

I just know that Reactive is Functional programming that mean declarative programming.

McDonal_11
  • 3,935
  • 6
  • 24
  • 55
alpiopio
  • 243
  • 1
  • 2
  • 10
  • You do not need to use any reactive library to build an app with mvvm pattern. I would also recommend NOT to use any of them only for that. 90% of projects that use RxSwift shouldn't really use it and RxSwift only complicates them. And if you just want an observable then just search on google for observable property wrapper and you will find something that has less than 100 lines of codes for sure. – Leszek Szary Aug 23 '21 at 06:16

1 Answers1

4

For me using delegate alone seems to be the longest way to implement your app logic. Going reactive is much more cleaner, requires less code and is a very powerful way of doing things.

Take for example, let say you want to create a Reactive ButtonTap system using RxSwift/RxCocoa with swift 4:

1) you create a protocol which is implemented the same way as you would when creating a normal delegate abstract class. Note that we are conforming to @objc

import RxSwift
import RxCocoa

// MARK: - ButtonTap Delegate protocol
@objc public protocol ButtonTapDelegate: NSObjectProtocol {
    @objc func button(didSelect view: UIView, at index: Int)
}

2) the DelegateProxyType class is where the reactive stuff happens. Just as you would have a class derive from your delegate (ButtonTapDelegate), this class not only does that but also handles your delegate messages using a PublishSubject.

// MARK: - ButtonTap DelegateProxy
open class ButtonTapDelegateProxy: DelegateProxy<UIView, ButtonTapDelegate>,
DelegateProxyType, ButtonTapDelegate {

    /// Typed parent object.
    public weak private(set) var buttonView: UIView?

    internal var didSelectSubject = PublishSubject<(view: UIView, index: Int)>()

    // MARK: - parent object for delegate proxy.
    public init(parentObject: UIView) {
        self.buttonView = parentObject
        super.init(parentObject: parentObject, delegateProxy: ButtonTapDelegateProxy.self)
    }

    // MARK: - Register known implementationss. (from DelegateProxyType)
    public static func registerKnownImplementations() {
        self.register { ButtonTapDelegateProxy(parentObject: $0) }
    }

    // MARK: - read the current delegate. (from DelegateProxyType)
    public class func currentDelegate(for object: UIView) -> ButtonTapDelegate? {
        return object.delegate
    }

    // MARK: - set the current delegate. (from DelegateProxyType)
    public class func setCurrentDelegate(_ delegate: ButtonTapDelegate?, to object: UIView) {
        object.delegate = delegate as? ButtonTapDelegateProxy
    }

    // MARK: delegate method
    public func button(didSelect view: UIView, at index: Int) {
        didSelectSubject.onNext((view, index))
    }

    // MARK: - dispose the publish subject 
    deinit {
        didSelectSubject.on(.completed)
    }
}

3) then, simply create your custom button class where you have the delegate property. You can simply pass a message to your delegate by calling its abstract method.

// MARK: - create Custom ButtonView class with the delegate property
open class ButtonView: UIView {
    @IBOutlet weak open var delegate: ButtonTapDelegate?

    func tapButtonAction() {
        let view = UIView()
        let index = 2
        delegate.button(didSelect: view, at: index)
    }

}

// MARK: - ButtonView must have delegate property
extension ButtonView: HasDelegate {
    public typealias Delegate = ButtonTapDelegate
}

4) you can capture that message sent by your delegate using RxCocoa. You would simply need to refer back to your PublishSubject from your DelegateProxy class using this extension of your view (ButtonView). Note that the extension is Reactive

// MARK: - Custom ButtonView with the Reactive delegate and its protocol function
 public extension Reactive where Base: ButtonView {

    /// Reactive wrapper for `delegate`.
    /// For more information take a look at `DelegateProxyType` protocol documentation.
    fileprivate var delegate: ButtonTapDelegateProxy {
        return ButtonTapDelegateProxy.proxy(for: base)
    }

    public func setDelegate(_ delegate: ButtonTapDelegate) -> Disposable {
        return ButtonTapDelegateProxy
        .installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
    }

    public var didSelect: ControlEvent<(view: UIView, index: Int)> {
        return ControlEvent(events: delegate.didSelectSubject)
    }
}

5) in your ViewController, you would listen to any button tap using RxSwift and capture those events sent by the PublishSubject in your DelegateProxy class. this is not different from the RxSwift gestures examples: https://github.com/RxSwiftCommunity/RxGesture

class myViewController: UIViewController {

    @IBOutlet weak var buttonView: ButtonView!

    override func viewDidLoad() {
         super.viewDidLoad

         buttonView.rx.didSelect.asObservable()
            .subscribe(onNext: { view, index in
             // button tapped in view at index
        }).disposed(by: bag)
    }

}

This process is very similar to how all the RxSwift and RxCocoa Reactive delegates work, which they have implemented on many UIKit elements like so: https://github.com/ReactiveX/RxSwift/tree/master/RxCocoa/iOS

Going reactive is just very flexible and powerful, there is no need to call delegate methods and set delegates all the time. This only happens once and just imagine the different combinations of how to handle your CustomViews inside your viewController.

George Quentin
  • 175
  • 2
  • 6