Unidirectional data flow architecture implementation for Android
Switch branches/tags
Clone or download
eepDev Merge pull request #51 from tmtron/CombinedReducer_ReferentialEquality
CombinedReducer.reduce: added instance check
Latest commit 0b3ba33 Jun 1, 2018

README.md

Build Status Codecov License Suas version Join the chat at https://gitter.im/SuasArch/Lobby



Analytics

Suas is a unidirectional data flow architecture implementation for iOS/macOS/tvOS/watchOS and Android heavily inspired by Redux. It provides an easy-to-use library that helps to create applications that are consistent, deterministic, and scalable.

Suas focuses on providing good developer experience and tooling such as customizable logging and state changes monitoring.

Join our gitter chat channel for any questions. Or check Suas documentation website.

What's on this page

For more in depth documentation on how to use Suas check Suas website, Suas API Interface or go straight to a list of applications built with Suas.

Suas application flow and components

Suas architecture is composed of five core elements:

  • Store: main component that contains a Reducer (or set of reducers), and the main application State. Listeners subscribe to it for state changes. Actions that cause state changes are dispatched to it.
  • State: defines the state of a component/screen or group of components/screens.
  • Action: each action specifies a change we want to effect on the state.
  • Reducer: contains the logic to alter the state based on a specific action received.
  • Listener: callback that gets notified when the state changes.

The following animation describes the Suas runtime flow.

Why use Suas

Suas helps you to build highly-dynamic, consistent mobile applications:

  • Cross platform; Suas-iOS works on iOS, macOS, tvOS and watchOS. Suas-Android works on all API levels and provides a Kotlin-friendly interface.
  • Focuses on developer experience with plugins/tools like LoggerMiddleware and Suas Monitor.
  • Small code base with low operational footprint. Check the source code 🙂.
  • Static typing and type information are conserved in the Store, Reducers, and Listeners.
  • Fast out of the box, and can be customized by developers to be even faster with filtering listeners.

Installation

Add Suas as a dependency to your build file:

Gradle:

# For Android Plugin 3.x and up
implementation 'com.zendesk.suas:suas:1.0.0'

# For Android Plugin 2.x and below
compile 'com.zendesk.suas:suas:1.0.0'

Maven:

<dependency>
  <groupId>com.zendesk.suas</groupId>
  <artifactId>suas</artifactId>
  <version>1.0.0</version>
</dependency>
Suas Suas version
Suas Logger Suas Logger
Suas Thunk Suas Thunk
Suas Monitor Suas version

Getting started

Let's get started by building a counter application.

When building applications in Suas, we start by defining the state for our counter. In this example, the counter state is an object that contains the counter value.

class Counter {

    private final int value;

    Counter(int value) {
        this.value = value;
    }

    int getValue() {
        return value;
    }
}

We then define the actions that affect the state. For the counter, we need increment and decrement actions.

private static final String INCREMENT_ACTION = "increment";
private static final String DECREMENT_ACTION = "decrement";

// ...

private static Action getDecrementAction(int value) {
    return new Action<>(DECREMENT_ACTION, value);
}

private static Action getIncrementAction(int value) {
    return new Action<>(INCREMENT_ACTION, value);
}

Now that we have both the State and the Actions, we need to specify how actions are going to affect the state. This logic is implemented in the Reducer. The counter state reducer looks like the following:

class CounterReducer extends Reducer<Counter> {

    @Nullable
    @Override
    public Counter reduce(@NonNull Counter oldState, @NonNull Action<?> action) {
        switch (action.getActionType()) {
            case INCREMENT_ACTION: {
                int incrementValue = action.getData();
                return new Counter(oldState.getValue() + incrementValue);
            }
            case DECREMENT_ACTION: {
                int decrementValue = action.getData();
                return new Counter(oldState.getValue() - decrementValue);
            }
            default: {
                return null;
            }
        }
    }

    @NonNull
    @Override
    public Counter getInitialState() {
        return new Counter(0);
    }
}

The reducer defines two things:

  1. The initial state for the store. i.e. the initial Counter value.
  2. The reduce function, which receives both the dispatched Action and the current State. This function decides what State to return based on the Action. If the reducer did not change the state, it should return null

The Store is the main component we interact with in the application. The store contains:

  1. The application's state.
  2. The reducer, or reducers.
  3. (Advanced) The middlewares

We create a store with the following snippet:

Store store = Suas.createStore(new CounterReducer()).build();

Now we can dispatch actions and add listeners to it. Let's see how it works:

public class SuasCounter {

    // ...

    public static void main(String [] args) {
        Store store = createStore();

        Listener<Counter> listener = (state) -> System.out.println("State changed to " + state.getValue());
        store.addListener(Counter.class, listener);

        store.dispatch(getIncrementAction(10));
        store.dispatch(getIncrementAction(1));
        store.dispatch(getDecrementAction(5));
    }
}

Let's break down the code above:

  1. We add a listener to the store by calling store.addListener(Counter.class, listener) specifying the state type.
  2. Then we dispatch different actions to the store calling store.dispatch(getIncrementAction(10)) and store.dispatch(getDecrementAction(5)).

That's it, check our documentation website for a full reference on Suas components and check the list of example built using Suas.

Developer experience and tooling

Suas focuses on developer experience and tooling. It provides two plugins in the form of Middlewares out of the box.

Customizable logging

While the LoggerMiddleware logs all the action received with the state changes.

Read more about how to use the LoggerMiddleware.

State transition monitoring

The MonitorMiddleware helps to track state transition and action dispatch history. When using MonitorMiddleware the Action dispatched and State changes are sent to our Suas Monitor desktop application.

Read how to install and start using the MonitorMiddleware by heading to getting started with monitor middleware article. Under the hood Suas Monitor uses the fantastic Redux DevTools to provide state and action information.

Example applications built with Suas

Check Suas website for an updated list of examples built with Suas.

Where to go next

To get more information about Suas:

Contributing

We love any sort of contribution. From changing the internals of how Suas works, changing Suas methods and public API, changing readmes and documentation topics.

Feel free to suggest changes on the GitHub repos or directly in Saus gitter channel.

For reference check our contributing guidelines.

Contact us

Join our gitter channel to talk to other Suas developers.

For any question, suggestion, or just to say hi, you can find the core team on twitter:

Suas future

To help craft Suas future releases, join us on gitter channel.

Copyright and license

Copyright 2017 Zendesk, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.