How to cook reactive programming. Part 3: Modularization.
Tags: unidirectionaldataflow, ios, architecture, combine
Published: 2021-07-23 08:58:06

Last time we were talking about different types of `Side Effects`, and moreover I showed you some libraries which support `Unidirectional data flow` approaches. I can certainly say if you’ve already read all the previous articles you now have a clear idea of how `reactive programming` works, and how to build a `unidirectional` architecture by yourself.

I would highly recommend firstly reading at least the two previous articles. However, if you’re not familiar with frameworks such as `RxSwift` or `Combine`, or reactive programming in general, I’d suggest reading my first article as well.

1. [What is Reactive Programming? iOS Edition](https://medium.com/atimca/what-is-reactive-programming-43e60cc4c0f?source=friends_link&sk=4ab8aa82f6e669bad59be42cba67e0ef)
2. [How to cook reactive programming. Part 1: Unidirectional architectures introduction.](https://medium.com/@atimca/how-to-cook-reactive-programming-part-1-unidirectional-architectures-introduction-5c73f3f7793d?source=friends_link&sk=56793e72690bb9703560cfc686d29aa7)
3. [How to cook reactive programming. Part 2: Side effects.](https://medium.com/@atimca/how-to-cook-reactive-programming-part-2-side-effects-2ce50f6fd966?sk=fd5ab407dd4bd71076944e8fb08c709f)

As I mentioned in my previous article, we live in a world where applications are not one button flashlight apps any more. We have teams of more than ten people. And if you noticed, the main idea of `Unidirectional architecture` is to keep all data in one place. I bet, if you start with this approach, you’ll end up with a huge `State` and `Reducer` if not at the end of the week, then certainly by the end of the month. You may wonder how it's possible to separate the `Unidirectional` approach on different modules when the main idea is to keep everything in one place. That’s exactly what I'm going to show you today.

Today I will show you two approaches for `Unidirectional architecture` modularization. I picked these two not because there are not more out there - you can easily find them if you search the internet. I picked them because they are opposite to one another. As a starting point, I picked a module that I want to use for experiments from the previous chapter.

```swift
struct User {
    let id: AnyHashable
}

struct NewsState {
    var user: User
    var step: Step
}

extension NewsState {
    enum Step {
        case initial
        case loading
        case loaded(data: [String])
    }
}

enum NewsEvent {
    case dataLoaded(data: [String])
    case loadData
}

extension NewsState {
    static func reduce(state: NewsState, event: NewsEvent) -> NewsState {
        var state = state
        switch event {
        case .dataLoaded(let data):
            state.step = .loaded(data: data)
        case .loadData:
            state.step = .loading
        }
        return state
    }
}
```

But this time we need an app, where all the magic will happen. I’d like to keep it as simple as possible.

```swift
struct AppState {
    var user: User
}

enum AppEvent {
    case newUserWasLoaded(User)
}

extension AppEvent {
    static func reduce(state: AppState, event: AppEvent) -> AppState {
        var state = state
        switch event {
        case .newUserWasLoaded(let user):
            state.user = user
        }
        return state
    }
}
```

Imagine you have a small module like I've shown you recently, which can download news titles. Let's just name it `News`. And you have the app itself, let's just call it `App`. Quite a simple module I’d say, but this time we need a `User` who’s shared within the whole app. Moreover, let's add a situation when the main app should know about the loading step from the news module, for example, it has another page, which depends on the news titles. So, let's start with the first approach on how to connect it to the main app. A list of requirements would look like this:

- The `App` needs to know about all changes in the `News`
- The `News` needs to know about all changes in the part of the `App` (`User`)

I think these requirements are simple and are enough for showing modularization concepts. So, let's start with the first approach.

## Composable Reducers

If you read my previous article, I bet you've already understood what architecture I'm going to talk about. [Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture.git) provides a really good way for modularization. What do they do?

The main idea of the modularization with the composable approach is to store every `Event` and every `State` in the `App` module. Let's change the app a little bit to conform to the new requirement.

```swift
struct AppState {
    var user: User
    var newsState: NewsState?
}

enum AppEvent {
    case newUserWasLoaded(User)
    case newsEvents(NewsEvent)
}
```

So far, I've just put everything inside the `App` itself. We’ll talk about problems with this approach a little bit later. For now, we need to proceed and solve the next problem. How would this system work? Maybe you've noticed that I omitted `reducer` for the new app variation. I did this on purpose because the entire work which should be done exists in the reducer itself.

Let's try to solve this problem as it stands with building a new reducer.

```swift
extension AppEvent {
    static func reduce(state: AppState, event: AppEvent) -> AppState {
        var state = state
        switch event {
        case .newUserWasLoaded(let user):
            state.user = user
        case .newsEvents(let event):
            switch event {
            case .dataLoaded(let data):
                state.newsState?.step = .loaded(data: data)
            case .loadData:
                state.newsState?.step = .loading
            }
        }
        return state
    }
}
```

It's a little bit clumsy, but it works. However, I've just generated another problem. `NewsState` is optional and should be created somewhere. Let's try to create it the first time when `.newsEvents` have appeared. The final `reducer` would look like this:

```swift
extension AppEvent {
    static func reduce(state: AppState, event: AppEvent) -> AppState {
        var state = state
        switch event {
        case .newUserWasLoaded(let user):
            state.user = user
        case .newsEvents(let event):
            if state.newsState == nil {
                state.newsState = NewsState(user: state.user, step: .initial)
            }
            switch event {
            case .dataLoaded(let data):
                state.newsState?.step = .loaded(data: data)
            case .loadData:
                state.newsState?.step = .loading
            }
        }
        return state
    }
}
```

Now the system works. However, it doesn't look like a modularization at all. All actions happen inside one function in the main application. Let's try to fix it a little bit. We've already had a `reduce` function for the `News` module. I want to try to adopt it as is to the previously implemented reducer.

```swift
extension AppEvent {
    static func reduce(state: AppState, event: AppEvent) -> AppState {
        var state = state
        switch event {
        case .newUserWasLoaded(let user):
            state.user = user
        case .newsEvents(let event):
            if state.newsState == nil {
                state.newsState = NewsState(user: state.user, step: .initial)
            }
            state.newsState = NewsState.reduce(state: state.newsState!, event: event)
        }
        return state
    }
}
```
Now we have the composition of the most primitive function. It looks better, but it’s still not what we would expect from modularization. Could we somehow improve this composition? Of course we can - in the end, I'm not inventing here anything.

I’d suggest starting with the functions declaration first. What do we already have? We have two reducers, which work like one reducer. What does that mean? It means we want a function which could `compose` two separate `reducers` into one. So, for the first step, we need a reducer declaration.

```swift
typealias Reducer = (AppState, AppEvent) -> AppState
```

Just a type alias for the function, which takes `State` and `Event` as the input and provides a result state as an output. Ok, so there’s nothing new here. For the next step let's make a declaration for the `combine` function, which combines multiple reducers into one.

```swift
func combine(_ reducers: Reducer...) -> Reducer
```

Things are progressing easily so far. Now with these declarations, we’ll write a realization for the `combine` function. We just need to chain every reducer with the provided state and event.

```swift
func combine(_ reducers: Reducer...) -> Reducer {
    return { state, event in
        reducers.reduce(state) { state, reducer in
            reducer(state, event)
        }
    }
}
```

This function could be realized with a simple “for” loop, however, the usage of the `reduce` function inside our `combine` looks more symbolic, doesn't it? Let's try out what we've done so far.

```swift
let firstReducer: Reducer = { state, event in
    var state = state
    switch event {
    case .newUserWasLoaded(let user):
        state.user = user
    default:
        return state
    }
    return state
}

let secondReducer: Reducer = { state, event in
    var state = state
    switch event {
    case .newsEvents:
        state.newsState?.step = .loading
    default:
        return state
    }
    return state
}

let resultReducer = combine(firstReducer, secondReducer)
let initialState = AppState(user: User(id: ""), newsState: NewsState(user: User(id: ""), step: .initial))

let firstMutation = resultReducer(initialState, .newUserWasLoaded(User(id: 1)))
let secondMutation = resultReducer(firstMutation, .newsEvents(.loadData))

print(secondMutation.user.id)
print(secondMutation.newsState!.step)

// Console output:
// 1
// loading
```

What have I just done? I've created two reducers, combined them into one result reducer, and performed two mutations. As a result you can see that two completely separated reducers mutate one state, which is a proof of concept. However, I created another problem, and I've started to have the feeling that I'm only creating problems, not solving them. The  `combine` function can work only over one type of state - `AppState`-  and one type of event - `AppEvent`. We have the `News` module, which has different state and event types. How could this situation be resolved? Maybe you've already guessed, according to my first iterations, that `NewsState`, now a part of `AppState` and `NewsEvent`, is a part of `AppEvent` and we could apply some conversions to fulfill `combine` function requirements. But what should we do exactly? We need to convert `NewsReducer` into `AppReducer`.

`NewsReducer` has this declaration:

```swift
func reduce(state: NewsState, event: NewsEvent) -> NewsState
```

And `AppReducer` this:

```swift
func reduce(state: AppState, event: AppEvent) -> AppState
```

I want to split this task into two smaller ones. Let's start with transforming

```swift
func reduce(state: NewsState, event: AppEvent) -> NewsState
```

into 

```swift
func reduce(state: AppState, event: AppEvent) -> AppState
```

As usual I want to start with a function definition:

``` swift
func transform(newsReducer: (NewsState, AppEvent) -> NewsState) -> Reducer
```

Because `Reducer` is a function itself, we need to build a function inside another function 🤯. The first step will look like this:

```swift
func transform(newsReducer: (NewsState, AppEvent) -> NewsState) -> Reducer {
    return { appState, appEvent in
        ???
    }
}
```

To build a function which uses `AppEvent` and `AppState` we have `NewsState` and `AppEvent`. What can we do here? Because we need to put `NewsState` inside `NewsReducer` we need to somehow transform `AppState` into `NewsState`:

```swift
func transform(
    newsReducer: (NewsState, AppEvent) -> NewsState,
    appStateIntoNews: (AppState) -> NewsState
) -> Reducer {
    return { appState, appEvent in
        let newsState = appStateIntoNews(appState)
        return newsReducer(newsState, appEvent)
    }
}
```

We’re Halfway there, but it's not enough, as we've got an error
> Cannot convert return expression of type 'NewsState' to return type 'AppState'

It means that we need somehow to transform the ews reducer result into `AppState`. We already know that `NewsState` is a part of the `AppState`, so it shouldn’t be so hard.

```swift
func transform(
    newsReducer: @escaping (NewsState, AppEvent) -> NewsState,
    appStateIntoNews: @escaping (AppState) -> NewsState
) -> Reducer {
    return { appState, appEvent in
        var appState = appState
        let newsState = appStateIntoNews(appState)
        let newNewsState = newsReducer(newsState, appEvent)
        appState.newsState = newNewsState
        return appState
    }
}
```

It will work, I can assure you. However, it doesn't look generic enough for usage for different applications with different state interfaces. So let's fix it.

```swift
func transform(
    newsReducer: @escaping (NewsState, AppEvent) -> NewsState,
    appStateIntoNews: @escaping (AppState) -> NewsState,
    mutateAppStateWithNewsState: @escaping (AppState, NewsState) -> AppState
) -> Reducer {
    return { appState, appEvent in
        let newsState = appStateIntoNews(appState)
        let newNewsState = newsReducer(newsState, appEvent)
        return mutateAppStateWithNewsState(appState, newNewsState)
    }
}
```

Much better, but we could refactor it a little bit more with a `KeyPath` usage, and decrease the number of input parameters of the function, so let's try it out.

```swift
func transform(
    newsReducer: @escaping (NewsState, AppEvent) -> NewsState,
    stateKeyPath: WritableKeyPath<AppState, NewsState>
) -> Reducer {
    return { appState, appEvent in
        var appState = appState
        appState[keyPath: stateKeyPath] = newsReducer(appState[keyPath: stateKeyPath], appEvent)
        return appState
    }
}
```

That's what I’m talking about. Let's move forward, we still have unfinished business according to events. The desired `transform` function will look like this:

```swift
func transform(newsReducer: (NewsState, NewsEvent) -> NewsState) -> Reducer
```

Let's start with an update to the existing one:

```swift
func transform(
    newsReducer: @escaping (NewsState, NewsEvent) -> NewsState,
    stateKeyPath: WritableKeyPath<AppState, NewsState>
) -> Reducer {
    return { appState, appEvent in
        var appState = appState
        appState[keyPath: stateKeyPath] = newsReducer(appState[keyPath: stateKeyPath], appEvent)
        return appState
    }
}
```

This code will show another error:
> Cannot convert value of type 'AppEvent' to expected argument type 'NewsEvent'

As far as you can see a Swift compiler really helps in these kinds of things, as throughout I'm just doing whatever it says to me. And now we need to convert `AppEvent` into `NewsEvent`. We've already made one as a part of another. Now we should make one small addition to make things easier.

```swift
enum AppEvent {
    case newUserWasLoaded(User)
    case newsEvents(NewsEvent)

    var newsEvent: NewsEvent? {
        guard case .newsEvents(let event) = self else { return nil }
        return event
    }
}
```

We have a `newsEvent` property, which allows us to easily convert `AppEvent` into `NewsEvent` if it's possible. I'm going to add this update and fix the error.

```swift
func transform(
    newsReducer: @escaping (NewsState, NewsEvent) -> NewsState,
    stateKeyPath: WritableKeyPath<AppState, NewsState>
) -> Reducer {
    return { appState, appEvent in
        guard let newsEvent = appEvent.newsEvent else { return appState }
        var appState = appState
        appState[keyPath: stateKeyPath] = newsReducer(appState[keyPath: stateKeyPath], newsEvent)
        return appState
    }
}
```

In this case, if the app event was a news event we will perform a news reducer. However, the same small change should be added to make the `transform` function more generic. What have we done so far? We converted `AppEvent` into `NewsEvent`, so should we just add a conversion function as another parameter of the `transform` function?

```swift
func transform(
    newsReducer: @escaping (NewsState, NewsEvent) -> NewsState,
    stateKeyPath: WritableKeyPath<AppState, NewsState>,
    toNewsEvent: @escaping (AppEvent) -> NewsEvent?
) -> Reducer {
    return { appState, appEvent in
        guard let newsEvent = toNewsEvent(appEvent) else { return appState }
        var appState = appState
        appState[keyPath: stateKeyPath] = newsReducer(appState[keyPath: stateKeyPath], newsEvent)
        return appState
    }
}
```
Perfect! Now we have a transform function, which will really help with the reducers composition. Shall, we add the final step to make `transform` and `combine` into generic functions?

```swift
func combine<State, Event>(_ reducers: (State, Event) -> State...) -> (State, Event) -> State {
    return { state, event in
        reducers.reduce(state) { state, reducer in
            reducer(state, event)
        }
    }
}

func transform<GlobalState, GlobalEvent, LocalState, LocalEvent>(
    localReducer: @escaping (LocalState, LocalEvent) -> LocalState,
    stateKeyPath: WritableKeyPath<GlobalState, LocalState?>,
    toLocalEvent: @escaping (GlobalEvent) -> LocalEvent?
) -> (GlobalState, GlobalEvent) -> GlobalState {
    return { globalState, globalEvent in
        guard
            let localEvent = toLocalEvent(globalEvent),
            let localState = globalState[keyPath: stateKeyPath]
            else { return globalState }
        var globalState = globalState
        globalState[keyPath: stateKeyPath] = localReducer(localState, localEvent)
        return globalState
    }
}
```
Voila! So it’s a small change, but we can use these functions for every reducer right now. Let's test them with our previous test case.

```swift
let appReducer: Reducer = { state, event in
    var state = state
    switch event {
    case .newUserWasLoaded(let user):
        state.user = user
    default:
        return state
    }
    return state
}


let combinedReducer = combine(
    transform(
        localReducer: NewsState.reduce(state:event:),
        stateKeyPath: \AppState.newsState,
        toLocalEvent: \AppEvent.newsEvent
    ),
    appReducer
)

let firstMutation = combinedReducer(initialState, .newUserWasLoaded(User(id: 1)))
let secondMutation = combinedReducer(firstMutation, .newsEvents(.loadData))

print(secondMutation.user.id)
print(secondMutation.newsState!.step)

// Console output:
// 1
// loading
```

The output is the same as on the previous occasion, however we now have a fully generic approach to achieve this. And that's it, we've achieved a fully generic way of `Unidirectional` architecture modularization. It's actually quite a straightforward way to modularize your system. Before we move forward to another modularization approach, let's talk a little bit about the pros and cons.

**Pros:**  
The true way of `Unidirectional` data flow. You don't lose any of the advantages of the unidirectional approach and as far as you can see the main mantra still applies - everything is in the one place, everything works in the one direction, everything is consistent. It goes from the previous point, there's no way - unless of course you deliberately make one - when you have for instance different users in different modules.


**Cons:**  
Not true module separation. To achieve this modularization approach you need to reveal the `State` and `Event` of the underlying module to the module where you attach things. It shouldn't be a big problem if all your modules work together only in one project or several affiliated projects. However, it could cause some problems in two cases. The first case is if you just have started to use unidirectional approaches in your codebase. I bet you want to isolate and reuse modules in this case, not connect them in one place. In the second case when you work over a framework, you shouldn’t expect your customers to adopt a way of modularization, which I've just shown to you.

As I've mentioned before, what I've shown you I took from the [pointfree](https://www.pointfree.co/collections/composable-architecture) channel. I've tried to make things a little bit different. They separated the app by modules, but I attached the existing module to the app. It’s not a big difference, but if you want to go deeper consider watching their videos as well. Also, they've explained a lot of functional programming concepts, which we used to achieve this modularization approach.

## Fully separated approach

Do you remember what were the problems of the previous approach? That it’s hard to use in isolation and you have to reveal a state and event for an upper module. With this approach, you can solve these problems.

For this time we need to remember about `Store`.

```swift
class NewsStore {
    @Published private(set) var state: NewsState
    private let reducer: (NewsState, NewsEvent) -> NewsState

    init(
        initialState: NewsState,
        reducer: @escaping (NewsState, NewsEvent) -> NewsState
    ) {
        self.state = initialState
        self.reducer = reducer
    }

    func accept(event: NewsEvent) {
        state = reducer(state, event)
    }
}
```

Why do we need `Store` here so badly? Because `Store` is an entry point of the whole module creation. We actually need it for `Composable Reducers`, but it’s not so bad and I wanted to save a little bit of your time.

So, how do we achieve modularization? The answer is actually more straightforward or even easier than with a previous approach. We just need to provide a way of updating information from the outside world and provide a way to notify changes from the child module. With any reactive framework, it's a super-easy task. So, let's implement it in one step.

```swift
class ViewController: UIViewController {
    var cancellables: Set<AnyCancellable> = []
}

enum InputEvent {
    case newUserWasLoaded(User)

    var user: User {
        switch self {
        case .newUserWasLoaded(let user):
            return user
        }
    }
}

enum OutputEvent {
    case newsStateStepWasUpdated(NewsState.Step)
}

class NewsStore {
    @Published private(set) var state: NewsState
    private let input: AnyPublisher<InputEvent, Never>
    private let reducer: (NewsState, NewsEvent) -> NewsState

    init(
        initialState: NewsState,
        input: AnyPublisher<InputEvent, Never>,
        reducer: @escaping (NewsState, NewsEvent) -> NewsState
    ) {
        self.state = initialState
        self.reducer = reducer
        self.input = input
    }

    func accept(event: NewsEvent) {
        state = reducer(state, event)
    }

    func start() -> (UIViewController, AnyPublisher<OutputEvent, Never>) {

        let viewController = ViewController()

        input
            .map(\.user)
            .sink { self.state.user = $0 }
            .store(in: &viewController.cancellables)

        let output = $state
            .map(\.step)
            .map(OutputEvent.newsStateStepWasUpdated)
            .setFailureType(to: Never.self)
            .eraseToAnyPublisher()

        return (viewController, output)
    }
}
```

What are all the nteresting things that happened in the `Store`? I've just added two publishers `Input` and `Output`. With input, I can observe what happened in the outside world of the module and update data, according to the input event. With output, I can cut a piece of the state, which could be interesting in the outside world. Also, there’s one small addition because the `sink` method produces `Cancellable` type, so we need to somehow utilize this `sink` subscription. I've attached it to the lifecycle of the view controller however, it could be done in different ways since unidirectional architecture could be used not only in the partnership with a view.

The second approach part ended up much shorter than the previous one. I hope it's because this approach is far easier to understand and to implement, not because I'm too lazy to write it step by step. Now let's talk about the pros and cons of this approach.

**Pros:**  
True modularization. An application doesn't depend on the underlying module realization. If you want to try unidirectional approaches, it's a way to adopt this as an only part of the project. Also, it’s suitable for SDKs creation.

**Cons:**  
It’s too easy to create the not true unidirectional state, because modules could have their own independent states and the whole app state could be inconsistent. Because it's a state-based system, sometimes you have to almost manually reset a state. Imagine the situation, that you need to open another scene from your child module. You have something like `sceneForOpen` property in the child module. The main module should send something, like `sceneWasShown`, back to the child module for clearing `sceneForOpen` property. Otherwise, if you go back and want to open the same page again nothing would happen, because the child's state hasn't been changed.

## What about Side Effects?

Side effects realization with modularization highly depend on the way you implement these side effects.

With the second fully separated approach there's no difference in handling side effects at all. You have a fully separated module with its own side effects, when you create this module side effects will work, as I've shown in the [previous article](https://medium.com/@atimca/how-to-cook-reactive-programming-part-2-side-effects-2ce50f6fd966?sk=fd5ab407dd4bd71076944e8fb08c709f). 

On the other hand, we have an approach with composable reducers. And thus I don't want to repeat the pointfree.co `Effects` approach to modularization. You can easily find an explanation in their videos or on [github](https://github.com/pointfreeco/swift-composable-architecture).

However, I want to show you at least something. Composable architecture has its effects system, but imagine the situation when you have composable reducers as a modularization approach and query-based side effects. Here's some code, showing how you could create a child store (child module) from the main store (main module). The main idea here is that every module has its own store, but every child module has a piece of this store from the main module.

```swift
    public func scope<LocalState, LocalEvent: ComponentableEvent>(
        state toLocalState: @escaping (State) -> LocalState,
        event toGlobalEvent: @escaping (LocalEvent) -> Event,
        feedBacks: [SideEffect<LocalState, LocalEvent>]
    ) -> Store<LocalState, LocalEvent> where LocalEvent.State == LocalState {

        let localStore = Store<LocalState, LocalEvent>(
            initial: toLocalState(state),
            feedBacks: feedBacks,
            reducer: { localState, localEvent in

                var state = self.state
                self.reducer(&state, toGlobalEvent(localEvent))
                localState = toLocalState(state)

                if localEvent != LocalEvent.updateFromOutside(localState) {
                    self.accept(toGlobalEvent(localEvent))
                }

        }
        )

        $state
            .map(toLocalState)
            .removeDuplicates()
            .sink { [weak localStore] newValue in
                localStore?.accept(.updateFromOutside(newValue))
            }
            .store(in: &localStore.disposeBag)

        return localStore
    }
```

In this case, we have a `scope` function which transforms the main store to the child one. Here `SideEffect<LocalState, LocalEvent>` is a pairing of query and action. The main idea of this approach is that while we create a child module, we inject side effects from it to the `scope` function. It gives us a way to construct modules with independent side effects. Every other action which is occurring in this function is mostly about main - child communication. It's necessary because we want to reflect changes from the main module inside the child one and visa versa.

There’s nothing hard to understand if you've already got how to handle modularization in general, trust me.

## Outro

As usual, there's no silver bullet for modularization. You can decide for yourself what to use. So far, you've become familiar with how to work and even how to build your own reactive framework and unidirectional architecture. Actually you know most of these things already. However, in my opinion, there's still one important topic left - testing. `Unidirectional` architectures give us a super elegant way of testing your code. How to do the testing I will show you in the next article. So, let's keep in touch!

If you liked this article don't forget to crash a clap button. Moreover, you can do it 50 times, for you, it's super easy, but it will be really helpful for me.

If you don't want to lose any new articles `subscribe` to my [twitter account](https://twitter.com/atimca) 

```swift
Twitter(.atimca)
.subscribe(onNext: { newArcticle in
    you.read(newArticle)
})
```