Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

Decompose — experiments with Kotlin Multiplatform lifecycle-aware components and navigation

 

 
Arkadii Ivanov
Android Engineer @ Badoo
Published: October 15, 2020
Tweet
Share
 

 

Kotlin Multiplatform is a technology that enables code sharing between different platforms. It is completely up to developers how much of the code they want to share. But there are cases for which we just can’t write shared code, even if we would like to.

One of the cases is UI. Currently it is not possible to make UI code common. And actually this particular case is totally fine — Kotlin Multiplatform’s intention is to share business logic, not UI.

Another case is navigation. We can implement all the business logic in common and add platform specific UI. But navigation between screens has to be also implemented separately in each platform. E.g. in Android we can use Badoo/RIBs framework or AndroidX Fragments. And in iOS we can similarly use ViewControllers.

But would not it be great to share navigation logic as well? So the only platform specific code we would have is UI (plus expect/actual).

 

Decompose

 

arkivanov/Decompose

Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI…

github.com

 

I have been experimenting with shared navigation for the last month, and this project is the result. Decompose is inspired by Badoo RIBs fork of the Uber RIBs framework. It is intended to address the following points:

  • Multiplatform: Android, iOS and JavaScript
  • Encapsulation of business logic into lifecycle-aware components (aka BLoCs)
  • Nested components
  • Routing (or navigation) between components
  • Components’ state preservation (including back stack)
  • Instances retaining in components (aka ViewModels in Android)
  • Pluggable platform-specific UI (primarily declarative UI frameworks, such as Jetpack Compose, SwiftUI, JavaScript React, etc.)

Let’s take a closer look at what the Decompose library offers.

 

Pluggable UI

Normally components (such as Android Fragments or iOS ViewControllers) encapsulate UI. But in Kotlin Multiplatform it is very important to allow pluggable platform specific UI. This is quite easy to achieve when using declarative UI frameworks, such as Jetpack Compose, SwiftUI or JavaScript React. Decompose is designed with this functionality in mind. Using normal Android Views is also possible, though.

The simple idea is to expose components’ state and to observe the state in the UI. Decompose provides Value and MutableValue interfaces and their builders and extensions for exposing state from components.

This is what a simple Counter component might look like. All it does is increase its value by one on an external event.

 

 

So in the simplest case, a component is just a class. That’s enough to be a component!

Here is an example of Android UI using Jetpack Compose:

 

 

And iOS UI using SwiftUI:

 

 

Lifecycle-aware Components

In Decompose each component has a Lifecycle. The Lifecycle is very similar to what we have in Android. This is how its interface looks like:

 

 

The Lifecycle can be in one of the predefined states and can be listened for changes. Here is the diagram showing possible transitions between states:

 

Image for post

 

The Lifecycle changes its state under various circumstances, such as the component is pushed to or popped from the back stack, or the application is minimized, etc. This works very similar to the AndroidX Fragment lifecycle, so you can read more about it in the documentation.

 

ComponentContext

In Decompose every component has a ComponentContext. It is nothing more than an interface that gives components access to some features, like the Lifecycle.

 

 

When we create a root component we have to create ComponentContext manually. There are various helper functions and default implementations to simplify this process. Child contexts are provided by the Router for every child component. When required, it can be passed normally via component’s constructor.

 

 

Nested components

Each component can encapsulate children components, so they can be represented as a tree. The Lifecycle of children can not be longer than that of their parents. So if a parent is destroyed then all its children are destroyed as well.

 

Image for post

 

Routing

This is the most interesting part. In Decompose routing functionality is provided by the Router. Here is the Router interface:

 

 

In general, the Router is just a stack of components. Currently only two operations are supported: push and pop. Each time a new component is pushed into Router, it is created and resumed, and the currently active component is stopped (but not destroyed) and is moved to the back stack. And when the currently active component is popped from the Router, it is destroyed and the most recent component in the back stack is resumed again. Each component can have multiple Routers.

This makes it possible to implement shared navigation logic.

 

Image for post

 

State and back stack preservation

This is very important especially in Android. When configuration changes (e.g. screen orientation) or process is recreated, all active components are restored in their previous state. Moreover, the back stack is also restored. This is possible because each component in the back stack has an associated persistent configuration. Configurations are defined by parents and are used for children instantiation.

Here is an example of routing:

 

 

Each Router has at least two arguments:

  • Initial configuration — a configuration of the very first component, that will be created and pushed to the Router if there is no saved state yet
  • Component factory — a function that creates components by configuration

Since all configurations are persistent, they are automatically saved and restored by the Router, and are passed to the component factory for recreation.

Decompose defines the Parcelable and @Parcelize interfaces using expect/actual declarations, so we can use them in common code. Parcelable implementations generator (aka Parcelize) works just fine with Kotlin Multiplatform. Saving state in other platforms is to be investigated.

Custom state preservation is also possible via StateKeeper API:

 

 

Instances retaining

In Android view models are retained (not destroyed) when configuration changes. This is used to keep existing data in memory and to avoid interrupting background tasks. Decompose provides similar InstanceKeeper API:

 

 

Samples

There are two sample projects available in the repository:

  • Counter — for Android, iOS and JavaScript
  • Todo List — for Android and iOS only

Both they cover many use cases, such as:

  • Nested components
  • Routing
  • State preservation and instances retaining
  • Pluggable UI (Jetpack Compose, SwiftUI, Kotlin React)
  • Multi-module structure
  • Inter-component communication

 

Library status

Decompose is version 0.0.7 at the time of this posting and is more like an experiment, let’s see how it goes. However, it is published and ready to try, might be a good fit for a pet project. Any feedback is appreciated.

 

arkivanov/Decompose

Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI…

github.com

 

Thanks for reading the article, and don’t forget to follow me on Twitter!

 

 

 

Tags: Kotlin, Android App Development, iOS App Development, Cross Platform, Kotlin Multiplatform

 

View original article at: 


 

Originally published: September 25, 2020

Android News
Compose CameraX on Android
Compose CameraX on Android

By Peng Jiang

Android new UI toolkit Jetpack compose is in beta now, which has all the features you need to build production-ready apps. CameraX is another Jetpack support library, which let you control the camera easier. As compose is still under development, lots of the views are still not available the compose way.

By ProAndroidDev -
Android News
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03

By Carlos Mota

It’s Monday, no releases this week, and… there’s a new version of Jetpack Compose — beta 03—available. What a perfect time to just increment 02 to 03 and see what’s new. The API is (almost) final so after updating from alpha to beta there weren’t any big changes to do. However, and remember that’s still in development, there’s always something that I need to update. 

By ProAndroidDev -
Android News
Noisy Code With Kotlin Scopes
Noisy Code With Kotlin Scopes

By Chetan Gupta

Scopes make your code more readable? think again... You are going to encounter these scope functions namely let, run, apply, also, within every Kotlin codebase, along with all the mischievous ways developers exploit their usage from the way they were intended for. Let see how popular opinion on those ends up just as a code noise.

By ProAndroidDev -
Android News
Improving Android DataBinding with Bindables library
Improving Android DataBinding with Bindables library

By Jaewoong Eum

DataBinding is one of the most important factors for MVVM architecture. The basic concept of DataBinding is to link the view and view model via observer patterns, properties, event callbacks, etc. Linking and automating communication between the view via the bound properties or something in the view model has a lot of benefits in the MVVM architecture concept.

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One