RxSwift DelegateProxy with required methods
May 12, 2016 -Once you’ve sipped from the Reactive Kool-Aid it can become emotionally painful to have to implement callback methods the old-fashioned way when you hit a delegate-style API that doesn’t have a prebuilt Observable
wrapper.
Max Alexander did a great tutorial post on how to implement your own DelegateProxy
for such an API, but unfortunately it won’t work for delegates that that have required methods. A common mistake is to implement the required delegate method in your DelegateProxy
class, forward the call using self._forwardToDelegate
, and then call rx_delegate.observe()
as before. This will result in the fairly misleading message:
Delegate proxy is already implementing ``xxx:``, a more performant way of registering might exist.
The right way to implement a DelegateProxy
for a delegate protocol with required methods involves doing the following (using the Google Identity Toolkit here as an example):
- First implement the
DelegateProxy
as per Max’s instructions, i.e.:
class RxGIDSignInDelegateProxy: DelegateProxy, GIDSignInDelegate, DelegateProxyType {
static func currentDelegateFor(object: AnyObject) -> AnyObject? {
let signin = object as! GIDSignIn
return signin.delegate
}
static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) {
let signin = object as! GIDSignIn
signin.delegate = delegate as? GIDSignInDelegate
}
}
- Next declare a local
PublishSubject
property in your proxy, and publishNext
&Error
events to the subject within the delegate method. You can see we’re also forwarding to the traditional delegate (if one exists), and for completeness’ sake, sendingCompleted
ondeinit
.
let signInSubject = PublishSubject<GIDGoogleUser>()
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
if let u = user {
signInSubject.on(.Next(u))
} else if let e = error {
signInSubject.on(.Error(e))
}
self._forwardToDelegate?.signIn(signIn, didSignInForUser: user, withError: error)
}
deinit {
signInSubject.on(.Completed)
}
- At this point, we can implement our
rx_*
extension method on the relevant class.PublishSubject
is a subclass ofObservable
, so we can just return this value directly.
extension GIDSignIn {
public var rx_delegate: DelegateProxy {
return proxyForObject(RxGIDSignInDelegateProxy.self, self)
}
public var rx_userDidSignIn: Observable<GIDGoogleUser> {
let proxy = proxyForObject(RxGIDSignInDelegateProxy.self, self)
return proxy.signInSubject
}
// implement your optional delegate methods with `rx_delegate.observe`
}
That’s it! Observe away without sullying your code with delegate callbacks.