Convert a Swift Delegate to RxSwift Observables

The Basics

class Thing: NSObject {
weak var delegate: ThingDelegate? = nil
}
@objc
protocol ThingDelegate: AnyObject {
// some number of methods
}
class ThingDelegateProxy
: DelegateProxy<Thing, ThingDelegate>
, DelegateProxyType
, ThingDelegate {
init(parentObject: Thing) {
super.init(
parentObject: parentObject,
delegateProxy: ThingDelegateProxy.self
)
}
public static func registerKnownImplementations() {
self.register { ThingDelegateProxy(parentObject: $0) }
}
}
extension Reactive where Base: Thing {
var delegate: ThingDelegateProxy {
return ThingDelegateProxy.proxy(for: base)
}
}

Optional Method, Passes a Parameter

@objc
protocol ThingDelegate: AnyObject {
@objc optional func thing(
_ thing: Thing,
hasDoneSomething param1: String,
with value: Int
)
}
var hasDoneSomething: Observable<(param1: String, value: Int)> {
delegate.methodInvoked(
#selector(ThingDelegate.thing(_:hasDoneSomething:with:))
)
.map { ($0[1] as! String, $0[2] as! Int) }
}

Required Method, Passes a Parameter

@objc
protocol ThingDelegate: AnyObject {
func thing(
_ thing: Thing,
hasDoneSomething param1: String,
with value: Int
)
}
func thing(
_ thing: Thing,
hasDoneSomething param1: String,
with value: Int
) {
hasDoneSomething.onNext((param1, value))
}
fileprivate let hasDoneSomething =
PublishSubject<(param1: String, value: Int)>()
var hasDoneSomething: Observable<(param1: String, value: Int)> {
delegate.hasDoneSomething
}

Method Returns a Value

protocol ThingDelegate: AnyObject {
func thingNeedsSomething(_ thing: Thing) -> String?
}
func thingNeedsSomething(_ thing: Thing) -> String? {
relay.value
}
fileprivate let needsSomethingRelay =
BehaviorRelay<String?>(value: nil)
var needsSomething: Binder<String?> {
Binder(delegate) { del, value in
del.needsSomethingRelay.accept(value)
}
}

Method both Passes a Parameter and Returns a Value

protocol ThingDelegate: AnyObject {
func thingNeedsSomething(
_ thing: Thing,
for item: String) -> Int
}
func thingNeedsSomething(_ thing: Thing, for item: String) -> Int {
relay.value[item] ?? 0
}
fileprivate let relay = BehaviorRelay<[String: Int]>(value: [:])
var needsSomething: Binder<[String: Int]> {
Binder(delegate) { del, value in
del.relay.accept(value)
}
}

Epilogue

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Daniel Tartaglia

Daniel Tartaglia

I started programming as a hobby in the late ’70s and professionally in the late ’90s. I’ve been writing iOS apps since 2010 and using RxSwift since 2015.