Library / SDK
square/workflow avatar
square/workflow

square/workflow: treating navigation as presenter state

A Swift and Kotlin library for making composable state machines, and UIs driven by those state machines.

1,168 stars73 forksShellApache-2.0

At a glance

What is it?
Workflow is a unidirectional data flow library that provides navigation and state management for Kotlin and Swift applications, and its distinguishing idea is that navigation is just another form of presenter state rather than a separate concern. This repository holds the documentation and the website; the libraries themselves live in workflow-swift and workflow-kotlin.
Who is it for?
Workflow fits a team building a multi-screen mobile application whose navigation has grown tangled and whose business logic needs to be unit tested, and it fits a team that already reasons in unidirectional data flow. It does not fit a project with one or two screens, where the indirection costs more than it saves.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 50 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Navigation as a state machine, not a special case

Workflow is a unidirectional data flow library that provides navigation and state management for Kotlin and Swift applications, and the sentence that explains its design is the one about navigation.

Workflow's main difference from its peers is its opinionated take on navigation, that navigation is just another form of presenter state to be managed, not a separate special concern.

The peers it names are TCA, the Swift Composable Architecture from Point-Free, Circuit from Slack, and Molecule from Cash App, and it says it can be compared to these and helped inspire them. Those are all unidirectional data flow architectures, and the difference is in what they each make the central abstraction. In many architectures navigation is a stack, a coordinator, or a sealed type the view layer interprets. Here it is state, and state is something a state machine already knows how to hold, validate and transition.

The rest of the feature list explains what follows from that choice.

It uses immutable data within each Workflow, with data flowing in a single direction from source to UI, and events in a single direction from the UI to the business logic. It supports writing business logic and complex UI navigation logic as state machines, thereby enabling confident reasoning about state and validation of correctness. It provides support for recursion, workflows of workflows of workflows, as its main idiom for navigation. And it is optimized for composability and scalability of features and screens.

Two further items make it a complete answer rather than a state machine library. It includes UI frameworks that bind Rendering data classes for views, including event callbacks, to mobile UI frameworks for Android and iOS. And it provides a testing framework that facilitates simple-to-write unit tests for all application business logic and helps ensure correctness.

That testing framework is the part that most directly changes a team's day, because it is the one that makes the rest of the design pay off.

This repository is the documentation, the code is two other repositories

The first thing to establish is what is in this repository, because it is not the library.

The top-level listing is `.github/`, `.gitignore`, `.markdownlint.rb`, `CHANGELOG.md`, `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md`, `LICENSE`, `README.md`, `RELEASING.md`, `deploy_website.sh`, `docs/`, `drawings.graffle`, `lint_docs.sh`, `mkdocs.yml`, `requirements.txt` and `wolfcrow.png`.

There is no source code. There is no build for a library, and the primary language of the repository is recorded as Shell, which is the `deploy_website.sh` script and the `lint_docs.sh` script.

The actual libraries are in two sibling repositories, `square/workflow-swift` for Swift and `square/workflow-kotlin` for Kotlin, and the README points you at each of them under Using Workflows in your project. There is also a Why Workflow page, a Glossary of Terms, a User Guide describing the core concepts, and a codelab-style tutorial for Kotlin and Android in the workflow-kotlin repository, with a Getting Started tutorial for Swift and iOS in workflow-swift, plus a set of Kotlin samples and Swift samples.

The documentation infrastructure explains the rest. `mkdocs.yml` is the site configuration, and `requirements.txt` pins the three Python packages the site is built with, `mkdocs` at 1.3.0, `mkdocs-material` at 8.2.11 and `mkdocs-redirects` at 1.0.4. The redirect plugin is a detail that says something about the site's history, since it exists to keep old URLs working after the documentation was reorganised.

`drawings.graffle` is the source of the architecture diagrams, and the diagrams in the documentation are therefore editable rather than being images committed by hand. There is a `lint_docs.sh` script and a `.markdownlint.rb` configuration, so the prose is linted alongside the code.

The practical consequence for an evaluator: this repository tells you what the library is for and how to think about it, and the code is one click away. If you are deciding whether to adopt Workflow, this is the place to decide, and the language repositories are the place to start using it.

Recursion as the idiom, which is both the strength and the cost

The recursion claim is the one that deserves unpacking, because it is what makes the library different from a state machine library with a navigation wrapper bolted on.

It provides excellent support for recursion, workflows of workflows of workflows, as its main idiom for navigation.

A typical mobile application has a screen that shows a list, pushes a detail screen, and the detail screen pushes a modal, and the back stack has to be modelled. In an architecture where navigation is a stack type, every screen that can be pushed to has to know about the stack, and the stack has to know about every screen. In an architecture where navigation is presenter state, each screen is a workflow that renders a state, and the state can contain another workflow's state, and the whole thing nests.

The benefit is that navigation becomes something you can test. A workflow that renders a screen and can transition to a modal is a function from state to state, and you can assert on the transitions without a UI test, without a device and without an integration test that clicks a button and waits. For a large application with deep navigation, that is the difference between a navigation layer that needs an end-to-end test suite and one that does not.

The cost is the learning curve, and the project acknowledges it by shipping a glossary and a Why Workflow page. The terminology is the barrier: workflows, renderings, running workflows, and the distinction between a workflow and a presenter all have to be learned before the code reads naturally.

The second cost is that the model is opinionated enough to be expensive to leave. Once navigation is expressed as nested workflow state, replacing it later is not a refactor, it is a rewrite of the screen layer. That is a normal property of an opinionated architecture, and it is worth naming before you commit rather than after.

A testing framework is the point, and the Slack channels are the support

Two parts of the repository are about the project as a working community rather than as a library, and both tell you what to expect if you adopt it.

The first is the testing framework, which is described as facilitating simple-to-write unit tests for all application business logic and helping ensure correctness. That sits alongside the claim about confidently reasoning about state and validation of correctness, and those two sentences are really one idea. A state machine whose states and transitions are all data can be tested as data, so the test is a function from state to state rather than a sequence of UI interactions, and the same test runs on any machine with no simulator.

The second is where the conversation happens. Workflow discussion happens in the Workflow Community Slack, with an open invitation link, and the maintainers also hang out in the #squarelibraries channel on the Kotlin Slack, with a sign-up link provided. The Workflow Community Slack being separate from the Kotlin one is the detail worth noting, because it means the library's community is not simply the Kotlin community, and the project has its own conversation space.

The support contact and community links sit in a section of the readme that also has a Support and Contact heading, and the community channels are the practical way to get an answer about the model rather than about the API, since the model is the hard part.

There is also a releasing and deploying section pointing at `RELEASING.md`, and the `deploy_website.sh` script at the root, which is the site's own deployment. That is a small sign of the project's shape: the site is a first-class artefact with its own linting, its own build and its own deploy step, maintained by the same people who maintain the libraries.

Where Workflow is the wrong choice

The first case is a small application. Workflow's costs are the vocabulary, the indirection, and the fact that navigation becomes part of your business logic model. For an application with one screen and a form, that is a large amount of structure for a small problem, and a plain view with a view model is the right answer.

The second case is a team that does not want to be opinionated. This is a library with a position, that navigation is presenter state, and that opinion is the source of both its strengths and its rigidity. A team that wants a stack-based navigation, or one that wants the platform's own navigation APIs to stay in charge, will find the library's model a fight rather than a help.

The third case is a team that cannot absorb a rewrite of the screen layer later. Because navigation is expressed as nested workflow state, and because the UI frameworks bind rendering data classes to the platform's view systems, moving away means rewriting the screen layer rather than replacing a component. That is fine for a long-lived application, and wrong for one you expect to change frameworks on.

The fourth is a shared codebase where the Swift and Kotlin implementations have drifted. The two language libraries are separate repositories maintained by the same team, and while the model is meant to be the same on both platforms, a feature implemented on one side and not the other is a real possibility in any polyglot mobile organisation.

One maintenance observation that needs stating carefully. The release history for this repository shows v0.28.1 from 2020-05-20, v0.28.0 from 2020-05-12 and v0.27.1 from 2020-05-12, and the last push was on 2026-09-03. The version numbers here track the shared library release, and the fact that the visible tags stop in 2020 while pushes continue into 2026 tells you the tags and the current state are not the same thing. The documentation site and the language-specific repositories are where you should check what is actually current.

Editorial conclusion

Workflow fits a team building a multi-screen mobile application whose navigation has grown tangled and whose business logic needs to be unit tested, and it fits a team that already reasons in unidirectional data flow. It does not fit a project with one or two screens, where the indirection costs more than it saves. Before you adopt it, read the Why Workflow page and the glossary, because the terminology is the learning curve, and note that the last tagged release of the libraries was v0.28.1 in May 2020, so you should check the language-specific repositories for what has changed since.

Frequently asked questions

What is the square/workflow library?

It is a unidirectional data flow library providing navigation and state management for Kotlin and Swift applications, where navigation is treated as another form of presenter state rather than a separate concern. It uses immutable data, supports recursive workflows of workflows, and includes UI bindings for Android and iOS plus a testing framework for business logic.

Where is the Workflow source code?

In two separate repositories, square/workflow-swift for Swift and square/workflow-kotlin for Kotlin. The square/workflow repository holds the documentation, the architecture diagrams and the website, with an `mkdocs.yml`, a `deploy_website.sh` script and a `lint_docs.sh` script.

How does Workflow differ from TCA, Circuit and Molecule?

The project names these as its peers and says it can be compared to them and helped inspire them. Its stated difference is its opinionated take on navigation, that navigation is just another form of presenter state to be managed rather than a separate special concern.

How do I get help with Workflow?

Discussion happens in the Workflow Community Slack, which has a public open invitation, and the maintainers also hang out in the #squarelibraries channel on the Kotlin Slack. There is also a Why Workflow page, a Glossary of Terms and a User Guide on the project site.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. square/workflow on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/square-workflow.svg)](https://hysenlabs.com/projects/square-workflow)

Was this useful?