iOS GUI Architecture: Concepts, Boundaries, and Common Pitfalls

When it comes to GUI architecture, the iOS platform offers several ways to build something solid and scalable (or not so much). In the market, we have options like MV, MVC, MVP, MVVM, for example.

But GUI architecture is not the only type of architecture used during software development, and many people get confused when naming things.

Early in my career as a programmer, I made many of these mistakes myself. Today, I’ll help you name each of these patterns and architectures correctly.

What Is Not a GUI Architecture

This section may seem unnecessary, but throughout my career, I’ve seen many people confuse and mix up the GUI architectures mentioned above with other concepts.
To be honest, I’ve made this mistake myself in the past.

GUI Architecture Is Not a Design Pattern

This needs to be very clear from the start: GUI architecture is not a design pattern, at least not at the same level of abstraction as real design patterns.

When we talk about design patterns, we’re talking about low-level patterns. Design patterns are local solutions to recurring problems in code.

You’ve probably heard of the Gang of Four, or at least the patterns they introduced, like Strategy, Observer, Factory, Decorator, and so on. That’s what design patterns are.

So what is GUI architecture? Because it’s obvious these are patterns too. And yes, they are patterns, but they are architectural patterns, not design patterns.

The scope of each type of pattern is completely different. While design patterns operate on classes, objects, and responsibilities, GUI architectural patterns span the entire UI of the software.

Architectural patterns define the separation of concerns, data flow, and dependency hierarchy. They operate at a completely different level. On top of that, an architectural pattern makes use of many design patterns internally (Observer, Strategy, Factory, etc.).

GUI Architecture Is Not Software Architecture

I’ve seen many people, when planning an app, say things like “Let’s use Clean Architecture instead of MVVM” as if they were mutually exclusive. They’re not.

GUI architecture exists solely to define how user interactions affect the model and how changes in the model are communicated back to the user.

Clean Architecture, for example, is usually layered like this: presentation, data, domain, application, infrastructure, and so on. VIPER, on the other hand, is composed of View, Interactor, Presenter, Entity, and Router. That’s the software architecture. It encompasses the entire application, including design patterns and GUI architecture.

The presentation layer is where we implement the GUI architecture, such as MVVM. In this layer, we have the View and the ViewModel. When the user interacts with the view, the ViewModel mediates communication with the model, typically through a use case, because of Clean Architecture, while still respecting both Clean Architecture and MVVM at the same time.

GUI Architecture Is Not State Architecture

I have to make a small mea culpa here. This is a mistake I made early in my iOS career. After reading some articles and watching a few videos by Thomas Ricouard about a Redux-like architecture for Swift, I implemented it in a few experimental apps, but at the time, I didn’t really understand what it was or when it should be used.

Basically, state architecture, unlike GUI architecture, defines where state lives, how it evolves, and who is allowed to change it. That’s basically it.

Just like GUI architecture is not mutually exclusive with design patterns or software architecture, the same applies to state architecture; they are all complementary. You can have an MVVM app, using Clean Architecture, and at the same time manage state with Redux, Observables, a State Machine, or so on.

GUI Architecture Is Not Flow Orchestration

This is a mistake I see recruiters make all the time when posting a job opening. They ask for experience with MVVM-C architecture, for example, but MVVM-C is not an architecture.

MVVM is, in fact, the Model–View–ViewModel architecture. The C stands for Coordinator.

A Coordinator is nothing more than a navigation and flow-orchestration pattern, just like Router and Flow. These patterns complement GUI architecture, but they don’t create a new one.

They’re also different areas of study. You don’t need to study MVVM-C specifically. Study MVVM to understand how the architecture works. Then study the Coordinators to understand how navigation works. Once you understand Coordinators, you can apply them to any UI architecture, whether it’s MVVM, MVC, MVP, VIP, or even VIPER (VIP and VIPER are not a GUI architecture; they are software architectures like Clean).

Even in VIPER, where the R stands for Router, you could choose to use Flow or Coordinator instead. Nobody is going to call it VIPEC or VIPEF. It’s still VIPER, just with a different navigation approach.

What GUI Architecture Is

GUI architecture is the set of rules that defines who decides what within the presentation layer.

It answers exclusively questions like:

  • Who reacts to UI events?
  • Where does presentation logic live?
  • Who transforms data into something renderable?
  • How does the UI communicate with the rest of the system?
  • Who knows about the framework (UIKit / SwiftUI)?

What a GUI Architecture Defines

  1. Clear roles
    • View
    • Controller / ViewModel / Presenter
  2. Communication flow
    • Who calls whom
    • Who observes whom
  3. Responsibility boundaries
    • What is presentation
    • What is not

Example:

  • MVVM defines:
    • The View observes
    • The ViewModel exposes state
    • The ViewModel does not know about the View

That’s GUI architecture.

Why Not Every Architecture Can Be Applied to a Framework

Before you say that it is possible to apply MVP, MVC, and so on to SwiftUI, let me ask you a question: does it actually make sense to use these architectures with this framework? What do you gain from it?

Conceptually, it makes no sense at all.

Let’s break down why.

MVC

  • In MVC, the view controller is responsible for telling the view to update
  • There is no view controller in SwiftUI

MVP

  • The presenter calls methods on the view
  • View methods are declared through a contract (interface/protocol)
  • In SwiftUI, views are structs
  • SwiftUI does not implement output protocols

VIP / VIPER

  • Passive view
  • Presenter pushes data into the view
  • Router is coupled to the screen’s lifecycle
  • In SwiftUI, data is not pushed
  • In SwiftUI, the flow is reactive

There are two major differences between UIKit and SwiftUI:

  1. SwiftUI is a state-driven framework. It is reactive.
  2. UIKit views are imperative.

The reason it’s possible to implement reactive MVVM in UIKit is that we use the living component, the ViewController, as the “reactive view.” It reacts to changes notified by the ViewModel and binds the new information to the views.

Let me emphasise this again: the ViewController represents the “View” in MVVM when using UIKit. You need to remember this. The UIView is not the View, the ViewController is.

Building Your Arsenal

That said, it becomes clear that, depending on the problem you’re dealing with, you can make use of one or more of these patterns, whether they’re design, GUI, state, or navigation related. Below is a list of some well-known and useful architectures and patterns:

FrameworkUI ArchSoftware ArchNavigation PatternState Arch
SwiftUIMVVMCleanCoordinatorRedux
UIKitMVCHexRouterMVI
MVPOnionFlowElm
MVVIP
VIPER

Some of these combinations naturally work very well, like SwiftUI + MVVM + Clean + Coordinator. Others may require a lot more boilerplate and become much more verbose, such as MVC + Redux, for example. But in the end, everything is achievable.

Conclusion

Don’t make the mistake of believing there’s such a thing as a bad architecture, and don’t go looking for a silver bullet either. Every case is different, even with SwiftUI, which naturally adapts well to MVVM.

MVC, which can turn into a mess in large projects, is great for MVPs (Minimum Viable Products) and small projects, since it’s less verbose and more centralized. MVVM, which, in my opinion, is one of the most balanced architectures, if not the most, can be completely unnecessary in some contexts. Imagine building a sample app that does nothing more than increment a value when you tap a button. Why use MVVM if you can just use an @State variable directly in the view?

With that in mind, I’ll say it again: there’s no bad architecture and no silver bullet. There are architectures that solve certain problems better than others. It’s up to you to analyze the case and choose what fits the project best.

Leave a comment