Unidirectional Architectures over ReactiveSwift - Part I: Redux

Since Facebook presented Flux in 2014, and its successor / alternative Redux was released by Dan Abramov and Andrew Clark, there has been a lot of hype around unidirectional architectures in software development world. I’ve recently released an initial version of my implementation of Redux over ReactiveSwift. This article makes a short (not really) summary of reasons why it’s done and why I think it’s a bit over other Redux stuff already written for Swift.

What problem do I (as well as other libs I’ll mention below) try to solve? The answer is pretty simple and obvious — state management. Nowadays, apps have grown so large that demand in predictable state management led people to creation of such kind of predictable state containers as Redux.

Redux in general

Unlike server-side solutions, client-side apps need other approaches to reducing overall solution complexity. It doesn’t matter, which specific platform the client application targets — browser or mobile — it should perform a really complex task of processing a huge amount of events: user input, platform state changes, network events and updates. Usually, an app has plenty of unobvious data flows within it: fire network request upon user input, meanwhile store some transient state in app’s storage, parse response and make another write transaction to app’s storage, enqueue another network request, while the first one is in progress if the user changes his mind with the input… With going mobile, certain things become even more crazy, cause multithreading jumps into this game. Mobile developers need to worry about making UI updates only on the main thread, avoid concurrency when touching the database, avoid deadlocks, etc. Long story short, one does not simply shate mutable state.

Original Redux docs end up with three principles for predictable state management. They are:

Single source of truth

State is read-only

Changes are made with pure functions

I will briefly go through Swift ports, which implement them:

ReSwift has been around for a while. It has something around 5k github stars and is up-to-date with all major Swift releases. Being ported closely from original JS implementation, this library adds few nasty Swift features, like generic state type, strong typing of subscribers, etc.

ReduxKit was also used widely, but has been deprecated in favor of ReSwift. There are also some less known implementations like this, this and this, but they’re rather excerpts from people’s projects and are not well-maintained to be used as community-proven solutions.

Overall, they suggest the following app data flow (called “unidirectional” for obvious reasons):

Here we see just a few details on implementation of the above principles:

Store is a single source of truth for the app’s State

State is readonly and observable by view when it’s subscribed to Store updates

State is modified by pure functions called Reducer’s. Having previous state and an action emitted by view, they calculate a new app’s state. Encapsulation hides reducers from view, so state changes are isolated.

Simplicity. While ReSwift covers aspects of observing, subscription / unsubscription, events dispatch, thread safety, interfaces, and protocols, my solution utilizes power of ReactiveSwift and is represented more or less by a single Store class. All the above mentioned stuff is out of the box for me, because I use MutableProperty wrapped inside Store.

Flexibility. My Redux implementation provides Store parametrized by generic State and Event types, with a little type extension for Defaultable type, allowing Store initialization without a default state if the state type provides a default value. ReactiveSwift’s PropertyProtocol and BindingTargetProvider protocols conformance enables to bind state / event streams with a simple <~ operator.

Power and ease-of-use. With ReactiveSwift’s Signal and SignalProducer for State, it’s ready for simple subscribing as well as for complex map, filter, reduce, collect, and other operations to write a truly declarative code with the smell of FRP.

Stricter requirements to State, no requirements to outer world. What do I mean by “stricter” — no optionals in either State or Event, no wrapping/unwrapping. What do I mean by outer world requirements— no protocol conformance needed to be subscripted to State changes. And yes, no protocols with associatedtype, so feel free to build any stores you like and create loosely-coupled solutions by using Dependency Injection (As a good example of DI framework I’ve used for Swift I can definitely name Swinject).

That’s pretty much it. Due to “no requirements to outer world”, this solution is pattern-agnostic, so it can be easily used as a model layer being a part of MVC, as a model and view-model layer as a part of MVVM, MVP, MV-Next-Big-Arch-Buzzword. The idea of a simple MVVM app (exactly like ReSwift example) can be found in Readme, I’ll get straight away to a more complex example.

Weather app using Redux

Such kind of an app is usually provided as a test task for candidates applying for junior iOS engineer position. They are asked to do a fetch of weather data for the current location using Gismeteo (or some other weather service) API. The weather forecast should be stored in a local storage. Usually, SQLite or CoreData are suggested as local storages, however it’s 2017, and apps don’t have memory limitations like those that were around for iPhone 3GS. There are strong alternatives, e.g. Realm, so we won’t strictly require any framework/implementation.

The next part of this article is mostly a tutorial of building this app using Redux and a couple of other tools, so if you want to skip it (danger zone: you might miss a few valuable insights) — here’s the link to the part of the article after the tutorial and here is the link to the full source code of the described example.

If you’re still here… let’s dive inside the tutorial!

Creating the project

This should look pretty similar to XCode templates. Let’s create a project called “Simple-Weather-App” and, for sure, check on Unit Tests checkmark.

Creating the project

For sure, we’ll use CocoaPods as a dependency manager (because I think, it’s the best dependency manager, Carthage and SPM aren’t that strong and widespread like pods).

Let’s go ahead and create Podfile in the root project directory. I usually use this template for multi-target projects and have nothing against you borrowing it for your own use :)

pod install --verbose in Terminal, close XCode project and open the newly created XCode workspace. We’re done with the most tricky part.

Designing our app’s UI

Let’s focus on which data we will show to the user: this should be — the date of the last weather update, the closest location, air temperature, real feel (if available), wind, rain, etc. This data can be fetched from AccuWeather API, which we select as a data source. Let’s show current conditions and forecasts for 5 day, switchable by segmented control. Data will be shown via a static table and has a horizontal pageable scroll. Let’s create a new file WeatherView.swift and WeatherView.xib (I don’t normally use IB and do layout in code, but for simplicity let’s use IB). Its layout is fairly simple:

WeatherView.xib

The screenshot shows the connected outlet, dataSource, and delegate for the table. I’ve also disabled safe area guides and trait variations, since this view is intended to be content-only and invariant relative to a size class. The table view needs cells to display content, let’s create some. We want a cell that will display our weather values, which can be in the following formats (information obtained by exploring AccuWeather APIs):

{Value} {Unit} // Single line of weather data, e.g. Air temperature: 68 F

We get the weather model with fields we would like to display. Now it’s time to get some weather JSON and start parsing. I won’t put full JSON here, since it’s pretty long, you can take a look at it here. I usually do Playground / Unit tests for parsing, so let’s go straight ahead and start our Unit tests! We will use Quick / Nimble over XCTest, since they allow writing more structured & human-readable unit tests. In terms of Quick, Test Suite is called “Spec”, so let’s create WeatherSpec.swift and start writing tests.

We haven’t implemented any mapping yet, so this file simply won’t compile. To make the mapping magic work, let’s utilize power of ObjectMapper. Get back to Weather.swift and implement ImmutableMappable conformance:

Cmd+U… Executed 1 test, with 0 failures (0 unexpected) in 0.012 (0.016) seconds! (Honestly saying, I did Cmd+U with fails like 10 times, until I’ve fixed all typos in the implementation, that’s why tests are especially useful for cases like parsing. Now I’m: 1) sure that my model is parseable from sample JSON; 2) If something is changed, test will fail).

More tests

That’s probably the most boring stuff, but I’m adding at least positive cases to check all parsed fields to make sure that sample parsing is correct.

Please note my extensive use of ! operator in unit tests code. Despite you’ve learned previously that force-unwrapping is very bad, I do it intentionally inside unit tests, because I want them:

To be simple and straightforward.

To fail much. They shouldn’t be error-prone like an app’s code.

Now let’s go ahead and add one more model, response, and spec to our app and tests — result of Geoposition search. We need this, because AccuWeather cannot provide us with weather data for particular GPS coordinates. Instead it provides weather data for the closest point we can find. Sample JSON is here, model and spec below.

Take a look at our tests for country flag transform. Do you see how easy they are for testing? That’s because we’re testing pure functions here — functions that produce predictable output for given input and have no side effect. We’ll recall this concept again when we’ll be testing our reducers.

Thank you for reaching out to Sigma Software! Please fill the form below. Our team will contact you shortly.

Full Name *

E-mail *

Phone

Company

Message

Page url

I hereby confirm that I am familiar with
Sigma Privacy Policy and agree to the personal data provided by me being stored and processed in accordance with the Policy
*

Petro

Korienev

Senior Software Engineer / Tech Lead

Petro is Senior Software Engineer / Tech Lead with focus on iOS software development. He's working with iOS solutions for more than 6 years and he's a big fan of applying functional reactive programming principles to iOS applications. Petro is the leader of CocoaHeads Ukraine - the biggest Ukrainian community of iOS/macOS/Swift developers.