RxSwift DelegateProxy with required methods

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):

  1. 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
        }
    }
    
  2. Next declare a 1ocal PublishSubject property in your proxy, and publish Next & 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, sending Completed on deinit.
        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)
        }
    
  3. At this point, we can implement our rx_* extension method on the relevant class. PublishSubject is a subclass of Observable, 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.

Advertisements

One thought on “RxSwift DelegateProxy with required methods

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s