TCA - An Alternative to MVVM?

1. Introduction:

SwiftUI's introduction marked a paradigm shift in iOS and macOS app development, encouraging developers to explore various architectural patterns. While MVVM (Model-View-ViewModel) has been a longstanding favorite, The Composable Architecture (TCA) is emerging as a compelling alternative, offering a unique approach to building iOS applications in SwiftUI.

The Composable Architecture (TCA) is a library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind. It can be used in SwiftUI, UIKit, and more, and on any Apple platform.

The main idea of this pattern is the unidirectional data flow that defines how data is managed and how state changes within the application. This pattern ensures a clear and predictable way of handling user interactions, state updates, and side effects.

Diagram comparing MVVM's two-way data binding with TCA's unidirectional data flow for state management in SwiftUI.

The image presents a comparison between two architecture patterns used in software development: MVVM on the left, and TCA on the right, as they relate to the development of user interfaces.

MVVM:

  • View: Represents the user interface, which displays the data. It observes the ViewModel for any changes to reflect in the UI.
  • ViewModel: Acts as an intermediary between the View and the Model. It processes the data from the Model to presentable formats and handles user inputs from the View.
  • Model: Refers to the data and business logic layer of the application.
  • The arrows indicate the direction of data flow and communication. The View and ViewModel have a two-way binding, meaning changes in the View can update the ViewModel and vice versa. The ViewModel retrieves and stores data in the Model, which has no direct communication with the View.

TCA:

  • View: Similar to MVVM, it displays the user interface elements and is reactive to state changes.
  • Action: Represents user actions or other events that can mutate the state of the application. These actions are sent to a reducer to produce a new state.
  • State: Maintains the current representation of all the data needed by the application or a portion of the UI. It is the single source of truth.
  • The flow of data here is unidirectional. The View dispatches Actions based on user interactions, which are processed by the architecture to produce new States, which in turn are reflected back in the View.conversation.
Visual comparison of MVVM's ViewModel interaction vs TCA's unidirectional flow for state and action handling

2. Understanding TCA

TCA, a relatively new entrant, proposes a holistic approach to application architecture. It is built on the principles of composability, state management, and effects handling. Its core components are:

  • State Management: Centralized and predictable state management, allowing easier tracking and debugging of state changes.
  • Effects: Side-effect handling in TCA is more controlled and predictable, allowing for better management of asynchronous actions like API calls.
  • Reducers: Reducers are pure functions that determine how the state changes in response to actions, promoting cleaner and more predictable state mutations.
  • Actions: These are simple data structures representing all the possible events that can change the state within the application.
TCA architecture diagram showing unidirectional data flow with side effects for managing asynchronous operations.

In the second image, we see a more detailed breakdown of TCA's flow:

  • The View initiates changes by sending Actions.
  • These Actions are processed by the Reducer, which resides within the Store.
  • The Reducer determines how the application's State should change in response to each Action.
  • The State is stored in the Store, which the View subscribes to for updates, creating a closed loop for state management.
  • The Effect boxes represent side effects that can perform asynchronous operations like API calls. These can dispatch further Actions to the Reducer, affecting the State and potentially triggering more Effects.

The architecture is designed to facilitate a clear and predictable flow of data, making the system easier to understand, debug, and test.

3. Basic Code Examples of TCA:

  • State - represents the entire state of a part of your application. It's a single source of truth and is usually defined as a struct in Swift.
 
  
@Reducer
struct Feature {
    struct State: Equatable {
        var count = 0
    }
}
  
  • Action - is a representation of all the events that can happen in your application. This includes user interactions, notifications from external sources, or any event that can change the state.
 
  
@Reducer
struct Feature {
    struct State: Equatable {
        var count = 0
    }

    enum Action {
        case incrementButtonTapped
        case decrementButtonTapped
    }
}
  
  • Reducer - function that describes how the state changes in response to actions. It takes the current state and an action as inputs and returns a new state. It is where the business logic of the app resides.
@Reducer
 
  
struct Feature {
    struct State: Equatable {
        var count = 0
    }

    enum Action {
        case incrementButtonTapped
        case decrementButtonTapped
    }

    var body: some Reducer {
        Reduce { state, action in
            switch action {
            case .incrementButtonTapped:
                state.count += 1
                return . none

            case .decrementButtonTapped:
                state.count -= 1
                return . none
            }
        }
    }
}
  
  • View - is the visible to the user and shows the latest state. View listens to user actions and pass the event as an action.
 
  
struct FeatureView: View {

    let store: StoreOf

    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            VStack {
                HStack {
                    Button("-") { viewStore.send(.decrementButtonTapped)}
                    Text("\(viewStore.count)")
                    Button("+") { viewStore.send(.incrementButtonTapped)}
                }
            }
        }
    }
}
  

4. Strong Points of TCA Architecture

  1. Modularity and Reusability:
    • Composable Components: TCA encourages breaking down the application into small, reusable components, making the codebase more manageable and easier to understand.
    • Code Reusability: Components are modular, allowing them to be reused across different parts of the application.
  2. Testing:
    • Top-Notch Testing Capabilities: TCA’s test stores and comprehensive testing functionalities make it easier to write and maintain unit tests.
  3. Functional Programming Principles:
    • Predictability and Clarity: Leveraging functional programming principles brings predictability and clarity to the code, reducing side effects and making the flow of data explicit.
  4. Unidirectional Data Flow:
    • Clear Data Management: Enforces a unidirectional data flow, simplifying state management and making the application logic more straightforward.

5. Weak Point of TCA Architecture

  1. Complexity and Learning Curve:
    • Steep Learning Curve: Requires a significant investment in learning, especially for those unfamiliar with functional programming or reactive programming.
  2. Performance Issues:
    • Stack Usage and Slow Action Processing: TCA can be stack-heavy and slow when processing actions, particularly in large applications. This can lead to performance bottlenecks.
    • Large Reducers: Reducers can become massive and unwieldy, leading to maintenance challenges and potential performance degradation.
  3. Architectural Misalignment with iOS SDK:
    • Functional vs. Object-Oriented: TCA's functional approach may not align well with the iOS SDK’s object-oriented roots, causing an impedance mismatch and potentially leading to inefficiencies.
  4. Lack of Encapsulation:
    • Global State Accessibility: Lack of encapsulation can result in any part of the codebase accessing and modifying global state, making the system more error-prone and harder to maintain.

6. TCA vs. MVVM

  • State Management: Unlike MVVM, where state management can be implicit and scattered across view models, TCA’s state management is explicit and centralized.
  • Testing: TCA's reducers being pure functions make them more testable compared to the view models in MVVM, which might have dependencies on other parts of the application.
  • Boilerplate Code: TCA might initially seem to introduce more boilerplate, but this structure leads to clearer and more maintainable code in larger projects.
  • Learning Curve: TCA has a steeper learning curve compared to MVVM due to its functional programming concepts, but it pays off in terms of application maintainability and scalability.

7. Conclusion

  • TCA (The Composable Architecture) offers a highly modular, reusable, and testable approach to building iOS applications with functional programming principles and unidirectional data flow. However, it comes with significant complexities, requiring a steep learning curve and continuous re-learning as the framework evolves. Performance issues, particularly in large applications, and the lack of alignment with the iOS SDK’s object-oriented nature can pose challenges. Additionally, the heavy reliance on a small team of maintainers and the risks associated with using a third-party framework further complicate its adoption in large, multi-team environments.
  • For smaller teams or applications, TCA's benefits might outweigh these drawbacks, providing a structured and predictable architecture. In contrast, larger organizations with extensive codebases and multiple teams might find more conventional architectures like MVVM or Clean Architecture more practical, balancing the need for discipline with ease of onboarding and maintenance.
  • Future exploration into simpler architectures that maintain clean boundaries while achieving similar goals as TCA may provide more sustainable solutions for large-scale projects.
Written by
Michał Gabrielczyk

Engaged in the Mobile team since 2021. Constantly developing his competence in the area of mobile applications for iOS. Enthusiast of simple and effective solutions provided by Apple.

No items found.