2020.06.29. 15h • Márton Braun
This is a project over two years in the making, and I’m happy to finally announce it to a broader audience. It’s been quite the journey.
The project started as the architecture for a single app back in mid-2018, and was then extracted into a library and spread internally to other projects within AutSoft, my employer at the time. Huge shoutout to them for supporting this work!
I eventually took ownership of it myself, and continued development externally. It has technically been open source for more than a year, but I only got around to preparing it for the general public and a shiny
1.0 version tag in the last couple months. So… Here it is!
Some of the main goals of this architecture:
- Give guidance on all aspects of the application, covering not just the View architecture,
- Clearly separate concerns between different layers and components,
- Always keep views in a safe and consistent state with ViewModels,
- Handle configuration changes (and even process death) gracefully,
- Make offloading work to background threads trivial.
While RainbowCake is heavily opinionated, it also encourages you to deviate from it as needed. Feel free to pick and choose the ideas and library artifacts provided according to your own application’s needs!
Overview of layers
Let’s take a look at RainbowCake’s layers first, the colourful chart of which - alongside Android’s confection-based naming traditions - gave the framework its name.
- Views (Fragments or Activities) represent application screens. They observe immutable state from their respective ViewModels and display it on the UI. They also forward input events to the ViewModel, and may receive state updates or one-time events in return.
- ViewModels store the current state of the UI, handle UI related logic, and update the state based on results received from presenters. They start coroutines for every task they have to perform (triggered by input events), and forward calls to their presenters.
- Presenters put work on background threads and use interactors (one or more) to access business logic. Then, they transform the results to screen-specific presentation models for the ViewModels to store as state.
- Interactors contain the core business logic of the application. They aggregate and manipulate data and perform computations. They are not tied to a single screen, but instead group functionality by the major features of the application.
- Data sources provide the interactors with data from various origins - local database and file system, network locations, key-value stores, system APIs, resources, and so on. It’s their responsibility to abstract away the underlying implementation from the domain layer, and to keep their stored data in a consistent state.
Now, a quick roundup of buzzwords, to give you a broad idea of what you’ll find within RainbowCake. For details, please take a look at the referenced documentation pages.
Dependency injection using the framework’s Dagger 2 support is recommended, but it also ships with support for Koin, and you can also choose to integrate either these libraries or any other ones yourself.
- Dependency Injection: summary page for available DI approaches
- Dagger support: setting up with the provided Dagger support
- Koin support: setting up with the provided Koin support
Threading is done by coroutines, which RainbowCake offers both utilities and various recommendations for.
- Threading: theory intro for threading with RainbowCake
execute: info about RainbowCake’s custom coroutine builder
- Tutorial: Presentation: info about going to background threads easily
- Flows: fetching reactive data from data sources
State and event handling is based on the Jetpack
LiveData classes, and is an MVVM setup with hints of MVI (such as enforcing a single view state).
- Tutorial: State handling: intro to state handling with the framework
- View state: designing view state
- Testing: unit testing state and event changes
Learn about RainbowCake
Handling View State and Events with RainbowCake, presented at Kotliners 2020, serves as an introduction to RainbowCake’s capabilities in the View layer, and explains some implementation details about it as well.
The largest publicly available demo project for the framework is the Guardian News Demo.
There is a series of articles about architecture being published on this blog which explain the design choices behind RainbowCake, and might provide you insight for how to work with the architecture. Here are the already published parts:
- Designing and Working with Single View States on Android
- Thoughts about State Handling on Android
- Thoughts about Event Handling on Android
Go ahead and take a look at the framework, see if you can use it directly, or if any of its ideas are useful for your own projects. Please respect the terms of the Apache 2 license if you take pieces of code from the RainbowCake libraries.
Do you have improvement ideas for the project? Open a PR or an issue for discussion on GitHub! Not only is the library itself open source, but so are the samples, starter projects, and notably, its entire documentation.
You can find the rest of the public material in the rainbowcake organization on GitHub.
You might also like...
Fragments have... Complicated lifecycles, to say the least. Let's take a look at these, and how they all fit into the world of Jetpack today, with LifecycleOwners, LiveData, and coroutines.
In MVVM-like view architectures, view state isn't enough to communicate from the ViewModel to the View. They have to be supplemented by some kind of events, which come with several challenges you should be aware of.
Describing the state of a screen is a common practice these days thanks to MVI popularizing the concept. Let's take a look at some examples of how you can design your state objects neatly using data classes and sealed classes, and how you can put them into practice.
Handling the state of UI correctly is one of the prominent challenges of Android apps. Here are my subjective thoughts about some different approaches, which ones I tend to choose, and why.