Skip to content

Demo with the basics of Composable Architecture (TCA)

Notifications You must be signed in to change notification settings

akashsoni01/OnlineStoreTCA

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NOTICE ⚠️

ReducerProtocol Migration will come pretty soon as part of the TCA series. Thanks for your patience!

Online Store made with Composable Architecture (TCA)

The purpose of this demo is to explore the main concepts of TCA. If this is your first time reading about it, I strongly recommend you to read first the README from the main repo and watch the Tour of TCA.

Motivation

TL;DR: Build an app with TCA not too simple nor too complex to study the most important use cases, and provide concise documentation to new learners.

I wanted to demostrate the power of this great architecture to build applications for Apple ecosystem, like iOS, macOS, etc. (btw, soon will be expanded beyond Apple world! 🚀).

However, if you want to start learning TCA, you will find a lot of articles describing a simple one-screen application to ilustrate the main concepts. Don't get me wrong, that's a great way to start, but I feel that we have a gap between very simple demos and real world applications like isoword that could be too complex to understand some other important use cases (like navigation and how reducers are glued).

In this demo I've implemented a minimal online store that is actually connecting to a real network API (https://fakestoreapi.com). We got a list of products available, we can choose to add an item to the cart, add more than one item like any other e-commerce app (like Amazon for example), and once you are ready to purchase, move to the cart and send your order to the server.

Of course, we are using fakestoreapi.com, which means your requests aren't going to be processed for real, but all the networks status are, and you can play with it to map what it would be working with network calls using TCA.

Even if this demo is not considered a real-world app, it has enough reducers to ilustrate how data should be glued in order to interact together and isolate domains that only cared for very specific components within the app (For example: Tabs -> Product List -> Product Cell -> Add to Cart button).

Additionally, I've created tests to demostrate one of the key features of TCA and how it makes a test to fail if you didn't capture the actual mutation of your state.

Note: Feel free to recommend any change that may be great to teach a concept in a better way or something that you consider should be here too! :)

Screenshots

Tabs

||

Cart

||

The basics

Archiecture Diagram

Example

Let's say that you have a simple app with two buttons, one will increase a counter in the screen and the other will decrease it. This is what will happen if this app was implemented on TCA:

  1. The view is presented in the screen. It shows the current state of the app.

struct State: Equatable {
    var counter = 0
}
  1. The user press a button (let's say increase button), that internally send an action to the store.

enum Action: Equatable {
    case increaseCounter
    case decreaseCounter
}
  1. The store & reducer require an environment object, that in TCA is just the object holding your dependencies. If you don't have any dependencies yet, just add an empty Environment.
struct Environment {
    // Future Dependencies...
}
  1. The action is received by the reducer and proceed to mutate the state. Reducer MUST also return an effect, that represent logic from the "outside world" (network calls, notifications, database, etc). If no effect is needed, just return Effect.none .
let reducer = Reducer<
    State, Action, Environment
> { state, action, environment in
    switch action {
    case .increaseCounter:
        state.counter += 1
        return Effect.none
    case .decreaseCounter:
        state.counter -= 1
        return Effect.none
    }
}
  1. Once the mutation is done and the reducer returned the effect, the view will render the update in the screen.

  1. To observe object in TCA, we need an object called viewStore, that in this example is wrapped within WithViewStore view.
  2. We can send another action using viewStore.send() and an Action value.
struct ContentView: View {
    let store: Store<State, Action>

    var body: some View {
        WithViewStore(self.store) { viewStore in
            HStack {
                Button {
                    viewStore.send(.decreaseCounter)
                } label: {
                    Text("-")
                        .padding(10)
                        .background(.blue)
                        .foregroundColor(.white)
                        .cornerRadius(10)
                }
                .buttonStyle(.plain)

                Text(viewStore.counter.description)
                    .padding(5)

                Button {
                    viewStore.send(.increaseCounter)
                } label: {
                    Text("+")
                        .padding(10)
                        .background(.blue)
                        .foregroundColor(.white)
                        .cornerRadius(10)
                }
                .buttonStyle(.plain)
            }
        }
    }
}
  1. View is initialized by a Store object.
ContentView(
    store: Store(
        initialState: State(),
        reducer: reducer,
        environment: Environment()
    )
)

If you want to learn more about the basics, check out the following video

Composition

TBD

Environment

TBD

Side Effects

TBD

Testing

TBD

More coming ...

About

Demo with the basics of Composable Architecture (TCA)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%