One of the most chocking ideas (at least for me) after adopt a Redux like pattern was that everything worked totally synchronous. The actions were processed one by one in the main thread, the state modified, the view was updated and the predictability was fantastic but: How to deal with asynchronous signals or remote API calls?
The two paths of the “Async way”
There are two main approaches to deal with async calls: action creators and middleware.
Both of them share the same functionality behind and it can be summarized in 3 main points:
There is a trigger to make them work: Of course, something needs to happen in order to call an async method but the nature of this trigger diffiers from middleware to action creators. Action creators are called directly and then the async code inside them is executed. Middleware async code is called only when some kind of action is detected.
The state is used to decide if do something: Before call the async code, a copy of the whole state is needed in order to check if the call is really needed or not. Both action creators and middleware injects a copy of the current state inside their implementations.
An action is dispatched: The actions are dispatched to notify the external world how the async call is going. For example, when the async method finish we dispatch the result (success or error) inside an action. Besides, we can dispatch a loading action before start the asyn call.
Translated to seudo code, these three points could be expressed as:
Both ReKotlin and ReSwift include an easy way to define middleware and action creators. Let’s take a look to both of them:
Action creators are simple methods with following the signature:
(state: State, store: StoreType) -> Action?
The state parameter permits to check the current state information and the store will be used to dispatch any actions generated by the async code:
The middleware is defined by a very scary signature. Considering the a dispatch function like:
(Action) -> Unit, a middleware can be defined as:
(DispatchFunction, () -> State?) -> (DispatchFunction) -> DispatchFunction
Weird, but let’s check the code to see that they are really simply to define and to use:
One interesting point is that you can define a list of middlewares and they will acts like an assembly line. For that reason, there are two kinds of dispatch functions inside them:
- dispatch: The action will be inserted at the start of the middleware chain.
- next: The action dispatched will continue to the next middleware in the chain or to the reducers.
Async calls can be implemented easily in ReSwift and ReKoltin app. Personally I prefer middleware because the async calls can be grouped together according to the type of API. For example, DeviceMiddleware to deal with bluetooth async calls, NetworkMiddleware for the remote API and LoggingMiddleware to register info about all the actions.