MVVM vs TCA

Aleksa Simic
May 30, 2024
8 min read

MVVM success stories

Ever since SwiftUI came out in 2019, it was quite natural for all of the iOS developers who started using it to use it in combination with MVVM architecture.

Apple provided us with property wrappers like StateObject and ObservedObject, so it was quite easy to use them for the view models and drive the SwiftUI view updates as soon as some Published property from the view model changes.

Although there is a pretty big hype about the Model View (MV) architecture right now, and people are saying that in SwiftUI you don’t need a view model component, because SwiftUI already provides that for you, the fact is that all of the biggest apps built in SwiftUI are leveraging MVVM architecture of some sort.

MV architecture or simply no view models in SwiftUI can work, but for small projects, as soon as the app starts to scale and unit testing and maintainability become more important, MV and no view model approaches easily fall behind the MVVM.

In my first ever SwiftUI app I had a chance to work on, one of the biggest forecast mobile apps in Switzerland, we managed to successfully migrate the whole codebase, which was pretty big, to SwiftUI completely by using it with MVVM architecture. To get more understanding of how big of a project it was, I will share with you that the app was initially written in Objective-C, then, at some point, completely rewritten in UIKit, and finally, when I came to the project, completely rewritten in SwiftUI.

Most of the issues we had back then in 2021. when this migration happened was not about MVVM at all, they were about SwiftUI. Because, at that point, when we could support only iOS 13.0, in which the initial SwiftUI release happened, we couldn’t use all of the fixes and improvements Apple introduced to SwiftUI in the meantime. Anyway, we managed to get around them by doing some “hacky” fixes or just using UIKit for those components.

Also, we were able to properly unit test the entire codebase because in MVVM we could easily inject mock services in the view models and unit test them. Because of that, after we released the SwiftUI version of the app to the clients, we only had around 20 bugs in total.

After that, in my digital agency, I had a chance to work on around 10 different iOS apps for our clients and we successfully delivered them using SwiftUI and MVVM. Some of them were smaller, while some of them were pretty big-scale, and we didn’t experience any issues with MVVM on any of those projects.

To conclude, I want to share one more MVVM success story. So I am actively working at the moment on one large-scale iOS app that is used by millions of people around the world. And guess what? It’s using MVVM. To be honest, it is combining MVVM with Clean architectures and modularization, both vertical and horizontal, but MVVM is the core of everything happening in the app.

Many large scale companies like Adidas, Crypto.com and The Browser Company are already using TCA in their production iOS apps.

Redux-like architectures and SwiftUI

At some point 4 years ago, while SwiftUI was still in its real stages, people in the iOS community started talking about how it would go much better with some Redux-like architecture with SwiftUI being a declarative framework. At that point, the guys from PointFree created just what the community wanted, Redux-like architecture called The Composable Architecture (TCA).

When I first saw it, I immediately wanted to give it a try. At that point, we were about to start the work on one iOS project for which I knew that it would be a large-scale one. I decided to go with TCA on that one.

So we started going through all of the available TCA courses online and implementing that on the real project in parallel. After 1 year of actively working with TCA on that project, these were our impressions about the architecture and how it works with SwiftUI:

Pros:

  • Well structured - basically in TCA, similar to how in MVVM we have view models that drive the business logic we have the Reducers which are responsible for the business logic. Additionally, TCA encapsulates all of the screen properties that are driving the screen in the State structure, and all of the actions that are dispatched from the View to the Reducer inside the Actions enum, which gives us even more structure.
  • Goes well with SwiftUI - As stated above, SwiftUI is a declarative framework, and that’s why it naturally goes well with TCA being based on Redux.
  • Very good setup for writing unit tests - By leveraging TCA components like TestStore and dependency injection setup, writing unit tests is a breeze in TCA
  • Really good dependency injection setup using the @Dependency wrapper

Cons:

  • Hard to grasp at first - there is a quite steep learning curve with TCA, it has a lot of library components and you have to understand how they work to be able to use it in the most optimized way and leverage its full power.
  • Abstracts a lot of things, it’s not good for beginners - Because in TCA there are many things which are happening under the hood, it is probably not the best architectural choice for someone starting with SwiftUI and iOS, because at the beginning, it is way better to start with basics and then build on top of that.
  • State is a single source of truth - While this is good for a lot of things, in some scenarios, when you need to drift off from the architectural structure to do something, it is pretty difficult to do it.
  • Really slow compile time - At some point, when the app scaled and codebase became pretty large, we started having issues with compile time, which became pretty big.
MVVM vs TCA

Why did we migrate back from TCA to MVVM?

When we started working on the app using TCA, we started slowly, but once got over the learning curve and got to speed, we started delivering features pretty fast and everyone was pretty happy about it.

But as the app scaled, problems started occurring. Eventually, after some time, we decided to slowly start refactoring views that are using TCA to the good old MVVM + Clean + Modularized architecture. A couple of days ago, I came across a post where Rod Schmidt is talking about his 3-year working experience with TCA: https://rodschmidt.com/posts/composable-architecture-experience/. Even though we have been working with it around for about 1.5 years, our experience is pretty much the same so I would like to share some things Rod is talking about in his post and compare them to our experience:

  1. It’s Complicated - It sure is! While we as a team enjoyed learning it, we came to an issue that it was pretty hard to onboard a new team member to grasp the concept of TCA, mostly if he is a junior iOS developer, and it was one of my concerns from the moment we started using it.
  2. The Churn - TCA gets a lot of updates, so you can easily be left behind using the old TCA version in your project. It’s something we experience on our skin. We were so focused on building the features and then when we decided to bump up the TCA version the changes were so groundbreaking it would take us a week to migrate the whole codebase, so we decided to stick with that old version of the library to the day.
  3. Architecture - There is no encapsulation and you can end up having massive reducers. On this one, I agree with one of the creators of the TCA library Stephen Celis who responded to all of the concerns Rod has in his posts, and that you can end up having massive anything that is holding your app business logic, like the infamous Massive View Controller and less famous Massive View Model.
  4. Performance issue - This is the thing that directly led us to the decision to move to MVVM. As the app grew, build time got super large. Also, as stated in the article, TCA uses a lot of stacks, so the debugging process of some issues can be difficult.

Those were the main reasons we decided to slowly fall back from the TCA to MVVM + Clean + Modularized architecture at some point. Initially, I wasn’t sure if that was a good idea because I was aware of just how powerful TCA architecture is. But after I saw that more people are having the same experience as us, I was certain it was the right move, and that it is better we made it now instead of when the app scales further.

Conclusion

While both of these architectures are pretty viable and can be used in large-scale iOS applications, I would recommend people to use TCA only if they have a team of highly skilled iOS developers who could understand everything that is going on under the hood and maximize what the library provides.

On the other hand, if you want to play a safe game, I would recommend you start with MVVM. Then, when the app starts to scale you can go and introduce Clean architecture on top of the MVVM, and then finally, if there is a need, you can introduce vertical and horizontal modularization. I can guarantee you that you will have no problems with scaling your iOS app.

Aleksa Simic
Co-Founder & CEO of Aetherius Solutions
Share this post
Trending

The most popular e-books and project templates

iOS engineers from all over the world found these e-books and project templates as the ultimate source of knowledge needed to build and scale their mobile apps

Blog

Latest blogs

Kotlin Multiplatform
Kotlin Multiplatform vs Compose Multiplatform in building production ready mobile applications
iOS
Is MV pattern in SwiftUI ready to replace MVVM?
iOS
Step by step guide for creating feature module targets in iOS apps