Tuesday, February 09, 2016

MiniRxSwift

I wanted to provide asynchronous networking for my StockFighterApiClient library, but I had 2 problems

  • Callback-based asynchronous programming is not nice. It quickly leads to nested callback hell
  • The Reactive Framework provides a much nicer async model, and RxSwift is great. I've used it in other projects and I much prefer it to ReactiveCococa, however RxSwift is a multi-megabyte download with dozens of source files, and I want to keep the ApiClient library as self contained as possible
So, I wrote a small miniaturised version of RxSwift which only contains a few of the core operators.

RxSwift and ReactiveCocoa get bloated by UI bindings and stuff like that which are completely unnecessary for the core Rx functionality, so it's great not to have any of those

It's a single file, approx 400 lines including comments and whitespace, and you can find it here:
https://github.com/borland/StockFighterApiClient/blob/master/MiniRxSwift.swift

It is missing a few things which might end up being important, but even if those get added it will still remain a single file and I doubt it'd ever get over 1000 LOC.

While this is silly wheel re-inventing, and in a serious project I'd just use RxSwift, I found that there were some benefits that came of it.

Protocol Associated Types

It was the first time I'd used protocols with associated types in Swift before. Implementing Observable/Observer as protocols in swift was an interesting journey learning about how associated types worked, and how they are different to generics.

Overall I found associated types to be profoundly strange compared to anything I'd used before in other languages.
I can appreciate that associated types can be more powerful and flexible than generics, but I couldn't help feeling that I didn't actually want or need that extra power/flexibility, and the cost and complexity it imposes (having to create type-erased wrappers such as Apple's AnySequence, or my Observer class) was annoying.
I can't help but think that this is going to be somewhat of a pain for people as they learn swift. I can imagine trying to explain to a new developer on my team why we have to write wrapper classes to bridge between protocols and generics, and I'm pretty sure that's going to be a very confusing and difficult to grasp situation for a newbie.
At any rate, I learned a fundamentally new (to me) concept. That doesn't happen very often once you get past your first few years of programming, so it's been well worth the effort.

Learning/Refreshing Rx knowledge

At work, we've got a lot of C# code that uses the Reactive extensions for .NET. I initially learned Rx way back when it was a pre-release binary dll that Microsoft shipped (I vaguely recall) privately as a helper library in some version of Silverlight or something like that.
I learned it primarily by reading the decompiled source code using Reflector, as there wasn't any documentation or even an official download of Rx for quite some time.

This ended up being great for me, as I got a deep understanding of exactly what is going on behind the scenes for each method and task. No matter how good the documentation for something is, there's no substitute for knowing that (for example) a method takes a lock just before it does some critical thing and when it releases it.

I was only really able to do this though, because Rx back then was much smaller and simpler. The Rx of today has gone through many years of Architecting, Refactoring, Indirecting, Abstracting and Organising. While I've no doubt that it's benefitted the project overall, the downside is that everything is now very hard to find and to learn.
For example, take a look at SelectMany - It's 1739 lines of code just for that class, and to understand it you also need to know how it's base class works.
In contrast, the old original code for SelectMany (known as flatMap in languages other than C#), was a single function, and looked a lot like the one in MiniRxSwift which is entirely self contained, has no classes, and is under 40 LOC including whitespace.
The real RxSwift has taken the approach of porting from C#, so it keeps mostly the same class hierarchy and complexity as the C# implementation, and is equally hard to find your way around.

Hopefully someone one day can look at MiniRxSwift and get a better understanding of Rx

P.S.

I wrote most of this from my understanding of how Rx works. I did look at the Rx documentation, and I definitely looked at the RxSwift source code along the way - not to copy it - but to learn how they were doing things with Protocol-Associated-Types. The swift compiler produces a lot of confusing error messages if you get your generics/PAT bridging code wrong and I wouldn't have been able to do it without some reference code to learn from.

Thanks RxSwift!