RxSwift.Lifecycle

Once in a while I’m reminded that good API design is of vital importance. Today was such a day. Everybody developing for either Android or iOS platforms knows that one often needs to run something when the view is loaded or when it will appear. Both platforms provide nifty ‘callbacks’ for this:

Android vs iOS lifecycle diagram; iOS: viewDidLoad, viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear, viewDidUnload; Android: onCreate, onStart, onResume, onPause, onStop, onDestroy

It is good practice to start listeners onStart and cleaning up onStop (or other equal pairs). This is all good, and if we want to share functionality we simply create a subclass of UIViewController or Activity and we put the shared logic there. Then comes along another – very unrelated – piece of shared functionality which we also need to put in there: oops! We have only single inheritance. Or, we want that same functionality in both Activity and Fragment, or UIViewController and UITableViewController. There are two problems:

  • We can not override the same method multiple times (without creating a dependency chain).
  • We can not put the logic in 1 shared subclass if there are multiple parents (possibly related by inheritance), since we must create subclasses per specific parent.

Essentially there are some options to workaround this:

  1. Wait until Apple and Google add lifecycle events. That way we can create our own listeners or delegates.
  2. Create shared managers/utilties which we then delegate the logic to with 1 single call in 1 subclass per parent type.
  3. Swallow our pride and create a whole chain of subclasses per root type and per functionality. This becomes nasty very quickly.
  4. Change the compiled or byte code of the top most parent type. By adding lifecycle events we are essentially doing point 1 ourselves.

From how above list is formatted and the title of the article you can guess my preference.

Intermezzo: RxSwift
Since I use Rx (Reactive Extensions) a lot I found I had the following pattern quite often:

var disposable: Disposable? = nil
func viewDidLoad() {
  disposable = getSomeNiceObservable()
    .map(someTransformation)
    .subscribeNext { print($0) }
}
func viewDidUnload() {
  disposable?.dispose()
}

The same holds for Android. Luckily RxSwift adds a rx_deallocated Observable to NSObject which emits a value and completes in deinit. Now above code could be written like this:

func viewDidLoad() {
  _ = getSomeNiceObservable()
    .map(someTransformation)
    .takeUntil(rx_deallocated)
    .subscribeNext { print($0) }
}

Much shorter, and no extra instance variables needed, Eureka!

But this only works for deallocation, and as we are humans, we leak memory. If we retain self in the Observable sequence (this is quite common to accidentally do) a retain cycle is created between the Observable and self, the NSObject. To help and not hinder the programmer, it would be much easier if we could cleanup if the view is dismissed!

Introducing RxLifecycle
I created RxLifecycle, which works much like how RxSwift creates rx_deallocated. It provides 6 new Observables. Using a process called swizzling we swap the methods that correspond to the lifecycle with our own methods which trigger a Rx.Subject which is exposed by a normal field in a Swift extension. This way two Observable<Bool> are exposed:

  • rx_visible: did the controller appear/disappear?
  • rx_willBeVisible: will the controller appear/disappear?

Those are then conveniently split into Observable<Void> per distinct event:

  • rx_willAppear
  • rx_willDisappear
  • rx_appear
  • rx_disappear

Example usage:

Observable.interval(10, scheduler: MainScheduler.instance)
  .takeUntil(self.rx_disappear)
  .subscribeNext { print("\($0)") }

Find the full source at https://gist.github.com/hermanbanken/4ac458aede6290b59a4acd5d52b81a6f.

Animating views on Android

Note to self: whenever you try to animate a view that is either GONE or has height 0 this will not work the first time. The moment the user touches the screen or an UI update is triggered only then will the animation start.

Of course I found this Stack Overflow question about not starting animations, but the view was defined as being VISIBLE. I also explicitly made the view visible in code – just to be sure – but this was not enough. One response suggested setting the height to something larger than 0. I gave this some thought, but instead of manipulating the height I put in

view.setMinimumHeight(1)
view.requestLayout

After some crazy hours investigating threads, creating many Handlers and Runnables I figured to give this height-thingy an extra try. And it worked, finally!

So, whenever you animate the height of a view starting from 0, use:

Animation animation = new SomeAnimation(view, 500, height);
view.getLayoutParams().height = 1;
view.requestLayout();
view.startAnimation(animation);

Spray JSON

So you’re making yet another Scala app and you need to parse or write some data to file/database/webservice.

What do you do? If you’re not already inside an (awesome) framework like Play Framework you will probably search “Scala JSON” and find something like Spray JSON. That’s what I did. So then you write:

import scalax.io._
import spray.json._
import DefaultJsonProtocol._

val jsonString = largeMapObject.toJson.compactPrint
Resource.fromFile("someFile").write(jsonString)(Codec.UTF8)

… and you get an exception upon either writing, or parsing this string after it was only partially written. You have several megabytes of this so just writing to a String will not work.

You will need a JSON printer that can output streams or handle writers. After searching I found Scala Stuff’s json-parser that has support for several JSON AST libraries like Spray. With json-parser writing large JSON objects is just a matter of:

import org.scalastuff.json.spray._
val writer = new PrintWriter(new File(file))
new SprayJsonPrinter(writer, 0)(largeMapObject.toJson)
writer.close()

and reading is just as easy:

val reader = Source.fromFile(file).bufferedReader()
val jsValue = SprayJsonParser.parse(reader)

Although this basic usage of Spray and Scala Stuff’s json-parser allow you to parse and write large JSON object, your machines memory is still a bottleneck as they keep the resulting JSON AST in memory. When dealing with huge JSON streams I recommend that you take a look at json-parser’s JsonHandler trait or Play Frameworks JSON Transformers.