Introducing Flow — Asynchronous programming made easy

Måns Bernhardt
Apr 16, 2018 · 6 min read

Today we are proud to announce the open sourcing of Flow, a Swift framework for building and maintaining complex asynchronous flows. Flow is modern, lightweight and composable, allowing you to write code that is more robust and easier to reason about. It has been field-tested and has matured over the course of many years while being extensively used in iZettle’s point of sale app to solve real problems.

If you ever wondered what this thing about reactive programming is, now is the time to give it a try. We hope that it will give you the boost in productivity and joy of developing software that it did for us.

So what is Flow about?

Lifetime management — Managing long-living resources

In Flow, the Disposable protocol is the standard way of handling clean-up after something is done. The protocol only declares one method dispose() to be called to perform clean-up:

protocol Disposable {
func dispose()

By returning a Disposable when starting long living processes, you have a standard way of handling clean-up:

extension UIView {
func showSpinnerOverlay() -> Disposable {
let spinner = ...
return Disposer {

let disposable = view.showSpinnerOverlay()

disposable.dispose() // Hide spinner

Event handling — Observing events over time

By calling onValue() on a signal you will start listening on new changes until you dispose the returned disposable:

let bag = DisposeBag() // Bag of disposables

// UIButton provides a Signal<()>
let loginButton = UIButton(...)

bag += loginButton.onValue {
// Log in user if tapped

// UITextField provides a ReadSignal<String>
let emailField = UITextField(...)
let passwordField = UITextField(...)

// Combine and transform signals
let enableLogin = combineLatest(emailField, passwordField)
.map { email, password in
email.isValidEmail && password.isValidPassword
} // -> ReadSignal<Bool>

// Use bindings and key-paths to update your UI on changes
bag += enableLogin.bindTo(loginButton, \.isEnabled)

Asynchronous operations — Handle results that might not yet be available

Working with asynchronous code can easily become messy as soon as you need to nest a few operations which depend on each other’s results. So similar to signals, Flow abstracts the idea of an asynchronous operation and encapsulates it into a Future<T> type representing a result that might not yet be available:

extension URLSession {
func data(for request: URLRequest) -> Future<Data> {
return Future { completion in
let task = self.dataTask(with: request) { data, _, error in
if let error = error {
} else {
return Disposer { task.cancel() }

Futures are highly composable and comes with many useful transformations:

func login(email: String, password: String) -> Future<User> {
let request = URLRequest(...)
return request).map { data in
User(data: data)

login(...).onValue { user in
// Handle successful login
}.onError { error in
// Handle failed login

Bringing it all together

class LoginController: UIViewController {
let emailField: UITextField
let passwordField: UITextField
let loginButton: UIButton
let cancelButton: UIBarButtonItem

var enableLogin: ReadSignal<Bool> { // Introduced above }
func login() -> Future<User> { // Introduced above }
func showSpinnerOverlay() -> Disposable { // Introduced above }

// Returns future that completes with true if user chose to retry
func showRetryAlert(for error: Error) -> Future<Bool> {
return Future { completion in
let retry = UIAlertAction(title: "Retry") { completion(.success(true)) }
let cancel = UIAlertAction(title: "Cancel") { completion(.failure(error)) }
let alert = UIAlertController(...)
// Present alert ...
return Disposer { // Dismiss alert }

// Will setup UI observers and return a future completing after a successful login
func runLogin() -> Future<User> {
return Future { completion in // Completion to call with the result
let bag = DisposeBag() // Resources to keep alive while executing

// Make sure to signal at once to set up initial enabled state
bag += self.enableLogin.atOnce().bindTo(self.loginButton, \.isEnabled)

// If button is tapped, initiate potentially long running login request
bag += self.loginButton.onValue {
.performWhile {
// Show spinner during login request
}.onErrorRepeat { error in
// If login fails with an error show an alert...
// ...and retry the login request if the user chose to
self.showRetryAlert(for: error)
}.onValue { user in
// If login is successful, complete runLogin() with the user

// If cancel is tapped, complete runLogin() with an error
bag += self.cancelButton.onValue {

return bag // Return a disposable to dispose once the future completes

Looking back — on being pragmatic

Power of abstractions

When working with UI there is also a need to clean things up after e.g. a presentation. This is especially true when working with a lot of closure based APIs. Where previously we were trying to solve this by using weak references, this was a never-ending chase of fixing memory leaks. The solution turned out to be a very simple idea, but yet so powerful, solved by introducing the Disposable protocol.

Increased productivity

Learn more

Our hope is that this article has inspired you to think about asynchronous programming in new ways. If you liked what you have seen so far, we encourage you to explore Flow further. If you find it useful, we welcome feedback and why not suggest or even contribute with your own improvements.

iZettle Engineering

We build tools to help business grow — this is how we do it.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store