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.