This is a call to action for developers to unite on a standard Kotlin Redux implementation with multiplatform support. ReduxKotlin.org was created to be a community driven solution that supports all of Kotlin's platforms. Kotlin Multiplatform + Redux is a solution that pulls more code into the shared space. Talk to include brief intro to Redux in Kotlin, how it can be used, and a sample app. Also a look into how Redux can fit into the new reactive frameworks Jetpack Compose & SwiftUI. https://reduxkotlin.org
31. MovieSwiftUI-Kotlin
Code moved to shared Kotlin:
• networking
• models
• app state
• actions
• reducers
• user preferences (local storage)
• save/restore state logic
• date formating
37. What’s Next?
• ReduxKotlin.org website, similar to redux.js.org
• Continue to flesh out sample apps, adding jetpack compose
• Explore using Android/iOS/React + shared Kotlin code
• Integrate with devtools
• Add contributors to the team
Hi everyone, My name is Patrick Jackson and I am an engineer at WillowTree. Today I am talking about ReduxKotlin.org. It is an implementation of Redux in pure Kotlin that supports multiplatform. Now many of you may be thinking “do we need another Redux library”? Well, I think the answer is yes! And it needs to support all of Kotlin’s platforms.
What is this talk?
The purpose of this talk is to present ReduxKotlin.org as the standard implementation of Redux in kotlin and to propose a central place for all things redux in kotlin. I will talk about this collection of libraries and how they can be used in Native mobile development. I will also talk about why Redux can be a good architecture for Multiplatform development today, and in the future with the new UI SDKs for Android and iOS.
This talk is not an intro to Redux. I will do a quick overview of redux, but If you are new to redux, I recommend watching a great talk from yesterday titled “Redux for Android” by Elliott Chenger. There is another talk by a Willowtree coworker from last year’s Droidcon titled “Redux on Android, the nitty gritty” by Nish Tahir. And there was another talk today by folks at Netflix using Redux, that makes 3 talks on redux at Droidcon this year, so I would say this is an architecture that is growing. During this presentation I will talk about how to use Redux to share code between Android & iOS. Also I will discuss how using Redux will setup your project so it is ready for Jetpack compose and SwiftUI in the years to come.
By show of hands, who is using Redux in native mobile development?
Who is familiar with Redux?
First a quick recap of how we got here.
Redux was created in 2015 by Facebook engineers Dan Abramov and Andrew Clark as a solution for state management in web applications. Its known for “predictable” state management and using a centralized state. In other words, the application state is contained in one global object. Redux is often thought of as related to React, but they are completely separate libraries that do work well together.
Redux follows 3 principals:
single source of truth
State is read-only
Changes to state are made with pure functions
By following these principles, state for the application becomes predictable and removes possibility of bugs due to stale state, or state being duplicated in multiple places within the app.
Redux consist of a store object that contains functions for dispatching actions, subscribing to state changes, and getting the state. The global state is contained within the store and can only be changed by an Action sent to the dispatch function. Actions are objects that may contain any data necessary for the new state or for side effects. Actions basically represent any change to the system. They can be UI events or success or failure from network calls for example.
(click)
Middlewares are functions that have side effects and do not change the state. Logging, networking, analytics and routing are examples.
(click)
The reducer is a pure function that creates a new state from the old state and the action.
(click)
Listeners that have subscribed get notified when a new state is created.
This pattern creates a clean Unidirectional data flow.
This is all of redux. It really is a basic pattern and a very small library, only 8 files for ReduxKotlin. If your new to redux it may seem complex, however, it is quite simple. Actions are dispatched, they go through middleware function that may or may not do something on the given action, then then reducer changes the state based on the action, and subscribers are notified that a change has occurred.
So redux started getting used by frontend web developers.
The simplicity and design of redux allowed an ecosystem of middleware, store enhancers, and dev tools to emerge. All of these links are libraries that add capabilities or improve developer experience for the web. They are all built by the community. There are hundreds, solving things such as doing asynchronous actions in a unit testable way, persistence, logging, hot reload of state, action replay, state visualization, integration with other frameworks, handling of forms, and many more. It really is amazing how many are out there. None of this would have been possible if there was not a standard implementation of Redux. For a statically typed language such as Kotlin, it is even more critical that a standard library exists to enable sharing of code. Now many or these libraries are specific to javascript and the web and would not make sense for native android or iOS development, but they can be a source of inspiration. There are some really interesting approaches to complex async tasks that create solutions that are unit testable. One example is called Sagas. It uses javascript generators to handle complex series of async actions in a way that is easy to read and write tests for. With Kotlin and suspend functions this could be implemented in a similar fashion. I’m sure there are many libraries that could be made specifically for Android & iOS development.
Redux quickly rose to become one of the most popular state management libraries for frontend web development.
(click)
It wasn’t long before the mobile world started borrowing the redux pattern for use in native applications. In early 2016 we began seeing talks and blogpost on Redux for Android, and redux for iOS. (Click) Since then many redux libraries have been written for Android and iOS. These are just some of the libraries on Github. Redux on Android has also been used by major companies, such as Netflix.
Ok, so Redux is a simple library and easy to implement, however, despite all these libraries Redux has not soared in usage as it has for the web.
I believe there are 3 main reasons for Redux popularity on the web:
A standard implementation of Redux
This allowed an ecosystem of middleware, store enhancers, and dev tools to grow
Redux solved a particular problem with React
Reacts declarative UI code works particularly well with Redux’s global state management.
Given that Google and Apple announced new Declarative UI frameworks this year, this is really interesting. There
will likely be a good use case for Redux with Jetpack Compose and SwiftUI. I will talk more on this later.
Dev experience with Redux on web is great
Hot reload & time travel reduce feedback cycles and speed development. I don’t think we will have hot reload like the web anytime soon on Android or iOS, but I do think there are some improvements to the developer experience that redux can help with.
Bringing it back to ReduxKotlin.org, ReduxKotlin was written with the hopes of becoming a standard library for Redux. Redux-Kotlin is written in pure Kotlin that supports all of Kotlin’s platforms. I created it during innovation time at my employer, WillowTree, while exploring multiplatform Kotlin architectures. The implementation is basically a port of the Javascript version, keeping the same structure and names. With Kotlin, we can write code in a more functional way than Java, and this make redux code more concise.
(click)
The main goal of this project is to provide a standard Redux library to the kotlin community. The reduxkotlin.org domain name and GitHub org have been created, so that it is not owned by any individual or company.
On the main repo I have outlined the core values to guide decision making for the project:
the core redux library will be a minimal implementation that other libraries can build upon
Modular development - following the example of javascript redux
Support for all of Kotlin’s platforms
Developed in the open and enable communication via open channels such as slack, github, etc
Not owned by an individual or company
By closely following Javascript Redux example, porting of other middleware and devtools will be easier. In addition sharing ideas, and possibly code, with web teams may be easier. There is no reason why you could not share code between Android, iOS, and web, using redux for state management.
I have written this initial implementation of redux, thunk, reselect, and a few other middlewares and sample apps. I see this as a starting point, and welcome any feedback and contributions.
If you are using Redux in Kotlin, please take a look. You will also find channels to communicate and collaborate on the main repo.
To be clear, reduxkotlin is written entirely in kotlin and supports all platforms that Kotlin supports. For those of you new to Multiplatform Kotlin, this is the ability to write shared code in kotlin and have the compiler target different platforms. Kotlin supports these platforms: JVM, Native(iOS, macOS, win, linux), Javascript, and webassembly. The shared Kotlin code is compiled to native executables for each platform. For JVM, it is bytecode, for native it is a native binary without a VM. It is still early days for Kotlin Multiplatform and is still in experimental status, however many of the fundamentals are in place, and some apps have been shipped to production.
(click)
There is a solid gradle plugin provided by Jetbrains to configure builds. Networking support with Ktor. Serialization with kotlinx.serialization, SQLlite support with SQLDelight, and LocalStorage with MultiplatformSettings. Even Debugging kotlin code in Xcode works through use of an Xcode plugin created by Touchlabs.
Being able to share code between these platforms and write it in a great language like Kotlin, can be really powerful. The value proposition of creating a standard Redux library is now much greater. With ReduxKotlin you can write much of your code in shared kotlin, and then write a very thin view layer in the platform’s language such as Swift or Javascript.
So how can we architect a multiplatform app using redux? This diagram shows the most basic way. Everything to the left of the dotted line is written in shared kotlin. This includes the store with its reducers, middleware, actions, the appstate. Also includes networking, localstorage, and database code. This is the heart of your app, the logic for deciding what to display, state management, and fetching and storing data. This also means you can test this code in your shared module, which is another reduction of work.
On the right side is the platform-specific code. You write activities, fragments, and ViewControllers on iOS that dispatch actions directly to the store, and subscribe to state changes
(click)
These then render the view based on the state object when a change occurs. This creates a unidirectional data flow which is widely regarded as beneficial. It can reduce bugs by not having components reach into the view to get state.
Having UDF makes the view a function of the state of the app, which is really easy to think about.
Now to the nuts and bolts of how to write this code. Getting started with redux is easy. All the reduxkotlin artifacts are published to maven central. They have been published with gradle meta-data, so all that is needed is to add the dependencies to your common source set in your shared module and the dependency is added to each platform. You may need to enable gradle meta-data for this to work with and there are instructions in the redux-kotlin README on how to do so.
Then we need to write the redux components
(click)
A Global app state is needed and this can be a data class
(click)
Actions must be created. Typically these are data classes as well. I like to group them as inner classes for organization, but that is not necessary. Any object can be dispatched as an action, so there is no need for implementing an interface here.
(click)
A reducer is needed to update the state. This is usually a when statement that switches on the type of the action object. It creates a new state. Using the Data class copy method is useful here. As you can imagine this function can quickly get really large. There are a couple of ways to address this. 1) have the actions to the reduction by implementing an interface, or 2) having multiple reducers
(click)
Middleware functions are created using this helper function. Middleware take the store, a next dispatcher, and an action as parameters. The next function here dispatches to the next middleware in the middleware chain, or to the reducer if there are not more middleware. In this simple loggingMiddleware, it logs the name of the action and then passes the action to the next dispatcher, which in our example is the reducer.
(click)
And a store must be created using the createStore function and giving it our reducer, an initial state, and our middleware. The applyMiddleware function here is what is called a store enhancer, and it creates the middleware chain with the given middleware functions.
(click)
Now the store is setup and actions can be dispatched to the store. Note there are other ways of creating these components. For example the reducers can be functions instead of vals. Or you can use classes, it just needs to conform to the typealias as defined in ReduxKotlin.
Now to our thin view layer on Android. The activity subscribes to the store in onCreate.
The subscribe function takes a lambda as an argument. Here the lambda is calling the render method with the current state object as a parameter. This lambda will be called every time there is a change in the state.
The subscribe function returns us a StoreSubscription function. This is a function that is used to unsubscribe to the store when we no longer want to receive state change updates.
This is typically done in the onDestroy method.
Calling the unsubscribe function removes the reference to this activity from the store, and avoids a memory leak.
Dispatching actions is done with the dispatch function on the store object. Here a UserLoggedIn action is dispatched, with a new user object. This is a contrived example, but one can see how this could be hooked up to a click listener.
So that code creates this architecture.
(click)
It gives us unidirectional dataflow, which is good, but this is just a starting point for redux on mobile.
There are a few problems you will quickly run into:
the state may not contain data in the format that your view needs, and we don’t want to be transforming the data in our view.
The render method is called on every state change. This may result in setting values on views that have not changed and unwanted UI effects.
We can do better.
What we really want is to only update the view when the substate the view cares about changes, and then to create a ViewState containing formatted data that is ready to be displayed. I like to call this the presenter function. It has one job - select substate, and pass ViewState to the view when it changes.
(click)
Then we need a view interface, so we can call methods on the view. This may just be one render method, but often we need more than one.
Using kotlin and some functional programming techniques, we can get some very concise code to write this parts. I’ve written a middleware called presenter-middleware to make this easy.
Lets look at an example from one of the sample apps from reduxkotlin.org.
Defining the interface for our view is simple. It is just a matter of defining methods for different states of the view. This is probably very familiar to a lot of people from other android architectures.
(click)
The presenter function is provided here. This Presenter type is from the presenter-middleware package. A default implementation is provided for the interface here named startPresenter.
Start presenter is defined as a val and uses the presenter function provided by presenter-middleware and takes the view interface as a type parameter.
(click)
The presenter function takes a PresenterBuilder as a parameter, and a presenter builder is a lambda with receiver that returns a lambda with receiver. I know, this is a bit unusual, but what you now have is a block of code with access to the instance of the view and the state.
Everything in between the double curly braces has access to the view and the state. The injection of the view and state are completed by the presenter-middleware. It takes care of subscriptions and lifecycle management. I’m not going to dive into the details of this, but if you are interested the source code is at reduxkotlin.org.
So inside this block we can write some really concise code for selection state and doing updates to the view. The presenter-middleware is built with memoized state selectors, so it only updates the view when a new value is given by the select statement. Let’s look at this more closely
The ‘select’ function is defined here. The select function takes a lambda that returns a substate from the app state.
The ‘then’ function is an infix function that defines an action to be taken when a new value for the substate is received.
The body of the then function is ran when the substate changes. Here we call showLoading if the state has marked isLoadingItems as true, and otherwise call hideLoading(). Showloading and hideLoading are methods on from the StartView interface. Because this is a lambda with receiver the view is the implicit “this” within the scope of the presenter function.
Now, the style of this code is not typical of what we usually write. However within this narrow scope, where we are only concerned with linking substate, to calling view methods, I think it is ok. It becomes clear what is happening in the presenter function. What is really interesting is there is no state in these functions other than memoized substates, which is abstracted by the presenter-middleware. And there is no lifecycle shenanigans, that is handled by the presenter-middleware.
Using presenter-middleware is easy
- include the dependency - it is available on maven central, so its a one liner. That is not shown here
- install presenter-store enhancer
(click)
That is shown here where the store is created. The compose function used here, allows passing multiple store enhancers to the create store function.
The store enhancer does a couple of things, including inserting a middleware function that allows managing subscriptions to views.
(click)
Next, a presenterLifecycleObserver must be used on any view with a presenter function. In your activity or view it would be done with this code. On iOS there is a similar code to connect ViewControllers.
And that is it, it is now possible write presenterfunctions and have view interfaces and the presenter-middleware takes care of managing subscriptions and memoization.
This is a very new pattern, and anyone is welcome to try it. There may be some situations where it is not a fit, and its something I’m going to continue exploring. That is all I am showing of presenter-middleware today. It is on reduxkotlin.org if your interested.
So the presenter-middleware can help us get this architecture with very concise code and can be added to a project very simply. This is the power of having an ecosystem and a standard Redux. Store Enhancers and middleware such as this can be published to maven or jcenter and added to projects very quickly and easily. Helping create this ecosystem is the goal of reduxkotlin.org.
I have two open source sample apps using this architecture on github. They both are multiplatform and support Android and iOS. One is a Name Game where you guess the breed of cats and dogs. It has a voice mode where guesses can be spoken.
The other is a ReadingList app where you can search for books and create lists. These are fairly simple apps, but more complex then a todo app. They have networking, scrolling lists, and asynchronous actions.
These are a good place to get started with ReduxKotlin. Both use the presenter-middleware.
Ok, so we can use Redux in apps today pretty well. And we can share a lot of Kotlin code between Android & iOS, and even other platforms if we’d like.
But frontend mobile development is changing soon. Both Google and Apple announced new frameworks for building UIs this year. I am talking about Jetpack Compose & SwiftUI.
Both of these use a declarative style that is radically different from the current XML layouts and Activities, and Storyboards and ViewControllers. In a few years, we may live in a world without Activities and Fragments, or ViewControllers on iOS.
So what happens to this shared code? Using MVP or MVx architecture, there will likely be a lot of refactoring needed.
(click)
The interesting thing about Jetpack Compose and SwiftUI is how they are similar to React for the web. They all have a declarative style that breaks UI into small components which are essentially functions that render UI from state. React & Redux have been a popular combination since 2016. Redux has, in part, been used to solve difficulty of sharing state between React components by providing a global state.
Can Redux integrate with Jetpack Compose and SwiftUI?
MovieSwiftUI is an open source app by Thomas Ricouard. Its one of the most complete examples of an app written entirely in SwiftUI. It allows you to browse movies and discover actors and upcoming movies. You can curate a wish list and seen list. It’s really nice. He wrote this entirely with SwiftUI and Reswift, a swift Redux library. I was able to fork this and pull the redux and other code into a shared kotlin module and it worked really well. (click) The video your watching is the kotlin version. Note there is some stutter and jank in this video and that is coming from some current limitation of the pre-release SwiftUI, not the Kotlin code. It is present in both the pure swift and the swift-kotlin version.
The shared kotlin code contains: (click)
networking, models, the app state, actions, reducers, user preferences, save/restore state logic, and date formatting.
The swift code is now only the UI layer, and the shared logic can be reused by other platforms, such as Jetpack Compose.
Now on to how to use redux in SwiftUI. I’m going to go through this quickly, being this is probably a room full of Android Engineers. Of course the source code is at reduxkotlin.org if you want to take a look. SwiftUI is still beta, so these APIs are subject to change, but this is one approach that works currently. The store can be passed as an environmentObject to the root View of the app. Every child of root will now have access to the store through use of the @EnvironmentObject property wrapper. The Store is an ObservableObject and will cause a redraw whenever there is a new state. Because of how swiftUI does a diff on the views before rendering and only updating views that have changed, this can be completed efficiently.
This can be further simplified by created a protocol that views can implement that requires a map and a render function. The map function maps from the global state to a Props type, which is the data formatted for the needs of that view. This is basically what I was referring to as ViewState earlier. This pattern is very similar to React-Redux.
The result are views that only have to implement the Props struct and map function. This pattern seems pretty scalable. There is not a lot of boilerplate, and the logic specific to this view is contained within the view struct. This is how the SwiftMovieUI app is structured. In this code snippet the state object is from shared Kotlin code. Here the Props struct and the map function are in Swift, however this could be in shared kotlin as well, and shared with other platforms, such as android or the web.
How about jetpack compose? How can integrate Redux with Jetpack compose? Pretty easily!
In this example a list of movies is displayed. The list of movies only needs to be updated when the list changes. This shows how a substate can be selected and observed.
This select function is an extension function on the store, and it creates what is known as an effect in compose. It will automatically redraw the movies when the movies in the global state change. It will also automatically manage subscriptions to the store. I’m not going to dive into the details of this extension function, but it is in the source code of the MovieSwiftUI-Kotlin on reduxkotlin.org.
Jetpack compose is not quite ready for a complete sample such as MovieSwiftUI, but we can test how global state will work with Compose functions.
This demo is sharing network and redux code with the MovieSwiftUI app and is a basic layout of the “Now Showing” tab. My plan is to build this UI out as Jetpack compose matures. All the source code is in the MovieSwiftUI-Kotlin on reduxkotlin.org.
Ok, its clear that Redux is a useful pattern today and likely in the future. What’s Next for ReduxKotlin.org?
I plan on starting a reduxkotlin.org website, similar to redux.js.org - howtos, best practices, FAQs, etc
(click)
Continue to flesh out sample apps, adding jetpack compose as it matures.
(click)
Explore a Android/iOS/React app using shared Redux code.
(click)
Integrate with devtools (chrome redux devtools)
(click)
Add contributors to the team. If there is any interest you can contact me. I think this is a good opportunity to contribute to an open source project.
To wrap up, ReduxKotlin.org aims to be the standard for Kotlin projects using Redux. Lets build an ecosystem. It is able to be used today, all of the source code is at reduxkotlin.org. Feedback & contributions are welcome. Thank you to WillowTree for innovation time to work on this. We are also hiring, so if you like building great apps with a great team check out willowtreeapps.com
#redux on the Kotlinlang slack