Executive Summary

My ignorance of the Swift custom of using the extension feature to explode the definition of a single protocol across several source files lead me to misunderstand how to use the subscribe method in RxSwift.

Details

As I’ve said before, one of the things I love about working on my current team at Oracle is the opportunity to learn new things and put them in practice. Currently this means learing Swift and RxSwift. While doing the usual TDD, I needed to write several tests that exercise the contract of Rx. This required a concise way to pass an argument to the subscribe method of Observable that allowed making assertions for the onNext, onCompleted and onError cases. As I say in my talk “Programming Platform Growth: Table Stakes or Deal Makes?” (which I am blessed to be giving as a keynote at JavaLand 2019), a key ingredient of programming platform growth is the welcoming culture of its developer community. I have a concrete example here that speaks well of the Swift community in that regard.

I was looking for examples of passing an obsever to the subscribe method, but all of them passed a single onNext handler. This post while educational, is indicative of what I could find.

observable.subscribe(onNext: {(element) in
  print(element)
})

What I wanted was to pass a complete implementation of the ObserverType with inline methods for its onNext, onCompleted, and onError functions. I posed the question in the RxSwift slack channel and the Swift #Using forum and received helpful answers from each. As a beginner, I failed to grasp that what looks like an anonymous interface implementation in Java is actually in invocation of an overloaded method. You see, in Swift, it is common to spread out the actual surface area of an API over many different source files using the Swift extension feature. This was pointed out to me in the swift.org forum post from Adrian Zubarev. So, there is an overloaded subscribe method defined in ObservableType+Extensions.swift:

public func subscribe(onNext: ((E) -> Void)? = nil,
                      onError: ((Swift.Error) -> Void)? = nil,
                      onCompleted: (() -> Void)? = nil,
                      onDisposed: (() -> Void)? = nil)

This is simply a bunch of optional closure parameters, each with their own signature and default value of nil.

Meanwhile, on the real-time help front, danielt1263 and Jeremy offered some very helpful answers that supplement the answer from swift.org, and which I actually decided to use. They suggested using the variant of subscribe that takes a closure that takes an Event and returns Void.

  1. let sub = try getObservable().subscribe({ event in
  2.    switch event {
  3.    case let .next(value):
  4.        onNextCalled = true
  5.    case let .error(error):
  6.        onErrorCalled = true
  7.    case .completed:
  8.        onCompletedCalled = true
  9.    }
  10. })
  11.