• This repository has been archived on 12/Oct/2021
  • Stars
    star
    482
  • Rank 91,212 (Top 2 %)
  • Language
    Kotlin
  • License
    Apache License 2.0
  • Created over 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Reactive Virtual DOM for Android.

⚠️ This repository has been archived and is no longer accepting contributions ⚠️

Domic — Reactive Virtual DOM for Android

Domic is an abstraction for Android UI layer that mirrors real Android DOM, but reactively.

It allows you:

  • Unify interactions with Android UI layer across codebase
  • Unit test UI-related code with in-memory implementation of Virtual DOM
  • Efficiently render complex state objects (MVI/Redux)
  • Enforce async-only interaction with UI layer across codebase
  • Reuse existing Android DOM: Views, Widgets, Layouts

Domic — DOMe like.

Table of contents

Motivation

Scaling Android app codebase and development is complicated. Part of that complication comes from constant need in interaction between app's business logic and UI.

In recent years, lots of progress has been made by community in adopting, inventing and redesigning patterns that other platforms use to solve similar problems: MVP, MVVM, MVI, Redux, etc.

With help of reactive libraries like RxJava combining streams of data and expressing complicated logic became easier.

It naturally shifted application code into a form of reactive cycle where app state is combined with user input and new state is produced for rendering. It was always a cycle, but it's more explicit now.

However we've found that existing approaches have problems with scaling for such reactive rendering cycles and we think there is something that can enhance them to fix those problems.

We think that Domic, a Reactive Virtual DOM, can be that enhancement layer.

It is important to say that there are existing projects that overlap with Domic's functionality but with different trade-offs. Please refer to Alternatives section for details.

State of the Project

At the moment Domic is an experimental project.

It means that Lyft is not using it in production yet. We do however think that this is a perspective direction that will help to shape, scale and move Android and client-side development further in general.

The project doesn't have a public release yet because we want to gather some feedback from community to make sure we didn't make major design errors as scope and internal complexity of the project are quite high.

We're planning to start shipping 0.1.0 version soon though, please stay tuned!

Right now we expect community members interested in this project to clone it and play with its source code and samples, submit issues and pull requests to shape the project!

Examples

Bind Real Document Object

Binding Signature

val nameOfDocumentObject: VirtualType = BindingType()

Practical Example

val search: EditText = AndroidEditText(v.findViewById(R.id.search))

Observe State

Property Signature

search.observe.textChanges: Observable

Practical Example

search
    .observe
    .textChanges
    .debounce(300, MILLISECONDS, timeScheduler)
    .switchMap { searchService.search(it) }

Change State

Function Signature

search.change.enabled(Observable): Disposable

Practical Example

searchState
    .map { 
        when (it) {
            is InProgress -> false
            is Finished -> true
        } 
    }
    .startWith(true)
    .subscribe(search.change::enabled)
    // `(Observable) -> Disposable` is an extension function Domic provides.

Testing

Binding Signature

val nameOfDocumentObject: VirtualType = BindingType()

Practical Example

val search: EditText = TestEditText()

Simulating State Change

search.simulate.text("search term")

Asserting State

assertThat(search.check.enabled).isEqualTo(false)

Key Take-Aways

Reusing Existing Android DOM

Domic does not require you to rewrite layouts or adopt a new framework to build UI.

Domic binds to existing Android Framework DOM (UI components: Views, Widgets) so you can continue to use all the tooling and libraries you're used to: Views, Widgets, Layouts, Support Library, Layout Preview, IDE, build system.

Threading

Domic takes care of threading.

You can observe state on non-main thread(s), you can change state from non-main thread(s).

This allows to minimize workload on main thread thus giving it more time for rendering and handling user input.

Tip:

You can run Presenter/ViewModel/Model/Reductor on non-main thread(s) and only use main thread for minimal amount of required interactions with real Android DOM.

Diffing

Domic takes care of computing diff between previous state and new one thus only rendering what is different.

Motivation: Android Framework already diffs some state changes, typically, simple ones, like TextView.setEnabled(). However there are relatively expensive state changes like TextView.setText() that are not diffed by Android Framework and can require re-rendering, computations, notifying listener(s) and so on.

Tip:

You can drop complex state objects on Domic (like Redux or MVI are designed to work) and let it figure what needs to be rendered.

Observing Shared State

Domic makes sure you can observe state of same property with multiple Observers.

Typical example would be observing clicks on Android View: Android View can only have one click listener at a time, subsequent Observer effectively detaches previous one from the View.

Domic however share()s the Observable allowing multiple Observers observe same state.

Testing

Domic is an abstraction.

Bindings to real Android DOM is just one implementation of that abstraction.

Domic has separate in-memory implementation of the Virtual DOM that let's you unit test your Presenter/ViewModel/Model/Reductor.

Domic provides synchronous testing API to ease simulating and checking state changes.

Tip:

You can take Domic further and rendered whole app in memory thus be able to run functional and integration tests in memory on JVM*!*

Type Safety

Domic is type safe.

Observing state and changing state encapsulated into separate types withing each Document Object. ie Button.Observe and Button.Change.

This allows one to have read-only or write-only reference to a Document Object, thus pushing type safety even further.

Tip:

You can take Domic further and maybe even create a new MV-design with clear separation of code that Observes and Changes UI?

Implementation Details

TODO

Integrating with Existing Projects

Integrating with Redux

See samples/redux

Alternatives

Terminology

DOM

DOM stands for Document Object Model. Term itself comes from early days of web development (1998), however it's abstract enough and pretty much reflects how Android UI system works: Layouts, Views, Widgets, XML-based markdown language, memory model.

Domic however as of now doesn't track child-parent relationship between Document Objects, instead Domic binds to real DOM objects thus leaving layouting to the real DOM.

Document Object

Since Domic is a Reactive Virtual DOM for Android, we use Document Object as a name for Widget/View/Component type or instance.

It is however valid to call them Widget, View or Component, but Document Object is preferred, specifically for reasons of possible multiplatform support and for sake of easier discussions with developers working on other platforms.

Diffing

Diffing is a process of computing difference between previous known state and new state. Every property that Domic allows to change for a given Document Object is going through comparison with its previous state, thus eliminating updates of real DOM if they're not required.

Rendering

Rendering is a process of reflecting in-memory state of Virtual DOM on the real DOM.

Credits

As many other things in the tech world: programming languages, libraries, cluster management systems, build tools, etc, Domic is not an invention, but rather a compilation of ideas and experience.

Domic was influenced by:

Special credit ❤️ goes to Juno Android team, specifically Igor Korotenko for development efforts in similar direction.

More Repositories

1

cartography

Cartography is a Python tool that consolidates infrastructure assets and the relationships between them in an intuitive graph view powered by a Neo4j database.
Python
2,949
star
2

scissors

✂ Android image cropping library
Java
1,841
star
3

confidant

Confidant: your secret keeper. https://lyft.github.io/confidant
Python
1,781
star
4

clutch

Extensible platform for infrastructure management
Go
1,670
star
5

react-javascript-to-typescript-transform

Convert React JavaScript code to TypeScript with proper typing
TypeScript
1,574
star
6

mapper

A JSON deserialization library for Swift
Swift
1,174
star
7

scoop

🍦 micro framework for building view based modular Android applications.
Java
1,036
star
8

Hammer

iOS touch synthesis library
Swift
642
star
9

protoc-gen-star

protoc plugin library for efficient proto-based code generation
Go
563
star
10

xiblint

A tool for linting storyboard and xib files
Python
522
star
11

flinkk8soperator

Kubernetes operator that provides control plane for managing Apache Flink applications
Go
508
star
12

metadataproxy

A proxy for AWS's metadata service that gives out scoped IAM credentials from STS
Python
450
star
13

cni-ipvlan-vpc-k8s

AWS VPC Kubernetes CNI driver using IPvlan
Go
357
star
14

nuscenes-devkit

Devkit for the public 2019 Lyft Level 5 AV Dataset (fork of https://github.com/nutonomy/nuscenes-devkit)
Jupyter Notebook
352
star
15

presto-gateway

A load balancer / proxy / gateway for prestodb
JavaScript
337
star
16

toasted-marshmallow

S'More speed for Marshmallow
Python
297
star
17

Kronos-Android

An Open Source Kotlin SNTP library
Kotlin
239
star
18

coloralgorithm

Javacript function to produce color sets
JavaScript
225
star
19

awspricing

Python library for AWS pricing.
Python
201
star
20

discovery

This service provides a REST interface for querying for the list of hosts that belong to all microservices.
Python
185
star
21

linty_fresh

✨ Surface lint errors during code review
Python
183
star
22

universal-async-component

React Universal Async Component that works with server side rendering
TypeScript
180
star
23

python-blessclient

Python client for fetching BLESS certificates
Python
112
star
24

goruntime

Go client for Runtime application level feature flags and configuration
Go
84
star
25

omnibot

One slackbot to rule them all
Python
80
star
26

lyft-android-sdk

Public Lyft SDK for Android
Java
72
star
27

high-entropy-string

A library for classifying strings as potential secrets.
Python
60
star
28

gostats

Go client for Stats
Go
56
star
29

pynamodb-attributes

Common attributes for PynamoDB
Python
52
star
30

bandit-high-entropy-string

A high entropy string plugin for OpenStack's bandit project
Python
48
star
31

Lyft-iOS-sdk

Public Lyft SDK for iOS
Swift
43
star
32

python-kmsauth

A python library for reusing KMS for your own authentication and authorization
Python
38
star
33

opsreview

Compile a report of recent PagerDuty alerts for a single escalation policy.
Python
29
star
34

atlantis

Terraform automation for GitHub PRs (private fork of runatlantis/atlantis)
Go
18
star
35

lyft-node-sdk

Node SDK for the Lyft Public API
JavaScript
16
star
36

fake_sqs

An implementation of a local SQS service.
Ruby
15
star
37

lyft.github.io

This is code for oss.lyft.com website.
TypeScript
14
star
38

dockernetes

Run kubernetes inside a docker container.
Dockerfile
12
star
39

kustomizer

A container for running k8s kustomize
Shell
11
star
40

python-confidant-client

Client library and CLI for Confidant
Python
11
star
41

collectd-statsd

collectd plugin to write to statsd
Python
10
star
42

lyft-web-button

Build an actionable, Lyft-branded button for your website
JavaScript
9
star
43

dynamodb-hive-serde

Hive Deserializer for DynamoDB backup data format
Java
8
star
44

syx

Python 2 and 3 compatibility library from Lyft.
Python
7
star
45

lyft-node-samples

Sample applications using Node.js for the Lyft Public API
JavaScript
7
star
46

android-puzzlers

Android puzzles for Lyft Talks and more.
Java
6
star
47

lyft-go-sdk

Go SDK for the Lyft Public API
Go
6
star
48

lyft-django-sample

An API integration example using Django and social-auth.
Python
5
star
49

python-omnibot-receiver

Library for use by services that receive messages from omnibot.
Python
4
star
50

osscla

Open Source Contributor License Agreement service
Python
4
star
51

code-of-conduct

Code of Conduct for Lyft's open source projects
3
star
52

CLA

Contributor License Agreement (CLA) for Lyft's open source projects
3
star
53

awseipext

AWS Lambda that extends the EC2 Elastic IP API
Python
3
star
54

heroku-buildpack-php

Shell
2
star
55

lyft-go-samples

Sample applications in Go for the Lyft Public API
Go
1
star
56

flask-pystatsd

flask extension to simplify the use of the pystatsd library
Python
1
star
57

eventbot

A slackbot to help organize events
Python
1
star