Why would there ever be one “pure” architecture to rule them all…
There is an ideology among software developers that the software architecture should be completely and completely independent and separate from the user interface that supports it.
He argues that one should be able to put any software architecture on top any User interface and everything should just work.
It’s a charming, almost seductive approach…
And I think this is absolutely wrong.
While reading, keep in mind that I am writing this from the perspective of an iOS application developer, specifically SwiftUI, and that we are discussing internal client-side application Architecture, not back-end architecture.
It is on purpose, but more on that later.
I wrote an article about SwiftUI and VIPER, where I went into all the reasons why I considered VIP unsuitable as an architectural choice for SwiftUI applications on iOS and Mac.
The bottom line is that the main mechanism behind VIPER is delegation. It was designed to pass actions and messages between objects, and to do so it maintains a direct set of references between each of those objects. (view, presenter, etc.)
In other words, VIPER was designed to slot neatly into UIKit with
UIViewControllers, It was designed for the way those objects are persisted in and around memory
UIViewController Life Cycle.
The problem is that SwiftUI
Views are not
UIViews, They are not objects, they do not have references, and they are constantly being created and disassembled to run the SwiftUI interface.
so when you could Make something similar to VIPER work with SwiftUI, you’ll have to jump through a lot of hoops and write a lot of glue to do this.
As mentioned in the article, I saw a code base where the developer was forced to add a ViewModel to his VIPER-VM code so he had a place to put his observable values. Talk about glue code.
There were other problems, but needless to say, all those issues I presented in that article confirm that the VIPER architecture is unsuitable for use in SwiftUI.
Unless, of course, you to like Writing lots of extra adapters and boilerplate code.
found it? okay let’s go.
Some might argue that there is nothing like VIPER really an architecture, and it’s just a set of objects whose job it is to take information from Real Massage it into a form suitable for consumption by the architecture and user interface.
I also disagree with this.
In choosing an application architecture I wrote that the application architecture basically boils down to the rules we use to decide How We split our code into separate components. why does that part of it go There And why does this part go Here,
We write classes and structures and functions with well defined roles and behaviors and responsibilities. This is a scene. she is a model. Here this code is a service which provides the model. And so on.
And This Code is what takes information from those services and puts it in the view for presentation. And He The code is responsible for executing a given set of tasks and manipulating the state of our application.
All those pieces: model; Services; inventory; view model; views and view controllers; modules and frameworks; All of them Compose the overall architecture of our applications.
Do you point to the basement of a building and say, “There! That’s Real architecture! Everything else, floors and rooms and entrances and exits and ceilings, that doesn’t count! ,
Not at all The building is a cohesive whole, the architecture is the sum total All of its parts.
And the same is true of an application.
This is a Gestalt.
Of course, one can go into the housing and swap out the furnace or water heater. but those items are massive created to be interchangeable
They are designed to provide the service given to the dwelling and they fit into a specific space in its overall architecture.
And if they need to be swapped out, I shouldn’t be forced to redo the whole house to do so.
he is a well designed architecture.
Also note that such services provide services to the application. they rarely drive application.
Frank Lloyd Wright was perhaps one of the most famous architects in American history, and one of his guiding principles was organic architecture.
For Wright, the architecture of a building had to fit into its environment. One carefully chosen size and style and material.
But practical elements also come into play. In the north, houses are usually built with high, pitched roofs to better shed rain and snow. They reflect the reality of the environment in which they are placed.
One could build such a thing in the southwestern desert, but to what extent? It is not suitable for that environment and, perhaps more to the point, it is often a waste of scarce materials.
In fact, in the Southwest we often want Capture That water, just don’t flush it.
So it’s not only a solution designed for a different environment, it’s a solution that’s completely Wrong For this.
Returning to VIPER, we saw how it was largely unsuitable for use in SwiftUI and the same is true of many other common architecture options.
Using the Redux-style “single source of data for the entire application” pattern can cause performance and update issues in SwiftUI when I increment a single number in the global application state and now the entire application view tree needs to be checked to see Whether what is required to be updated and submitted.
Others were designed to solve problems inherent in languages other than Swift.
For example, immutable data and data flows are designed to protect against changes in languages that are based on reference types and do not have Swift’s copy-on-write semantics. For more on that see Case Against Immutable Objects in Swift
I could go on but I think you’re starting to get the idea.
In many ways UIKit reinforced the idea that there was a clear boundary between our “architecture” and our user interface code.
Anyone was free to choose VIPER, or MVP, or MVVM, or clean, or even full RxSwift data flow for their applications.
And thus the line between architecture and UI seemed clear and well defined. the architecture was all other Stuff we’ve added on top of UIKit.
But almost all of them were options created (or redesigned) to work with UIKit. They expected reference types and targets and delegates. He expected Views and ViewControllers to remain.
And most are expected to live in an imperative world.
But the world has changed.
SwiftUI is declarative, not imperative, and as such it takes into account how “views” are constructed and managed.
It holds opinion about the idea of a single source of truth for a particular element’s position.
This kind of state is thought to be about binding as little as possible in the hierarchy in order to reduce excessive view update cycles and rendering.
It is the opinion about how that state should be represented
@Binding Property wrappers, about how one defines and uses
ObservableObjectsAnd about how we can receive events and updates through Combine.
And it’s also starting to express its preferences for tasks and use async/await.
Ignore those thoughts and you end up working against How SwiftUI sees the world at best, and completely useless applications at worst.
The world, the environment has changed.
And that’s why I keep coming back to how well a specific architecture works with SwiftUI.
because if it Not there Works well with SwiftUI, can it really be universal? Can I really take any architecture I’ve designed for UIKit first and wrap it around SwiftUI and expect a good result?
I think I have demonstrated rather conclusively that you cannot.
When the world changes, architecture needs to change too.
So, let’s get back to our basics. Is there a pure “universal” architecture?
Well, from a theoretical Approach I agree that any type of architecture can be used anywhere… I mean, anything is possible.
But at some point our architectural choice has to change to the platform and integrate with the environment.
And so we come to the question, how much work does that integration put in? How much “glue” do we need? and is it working with environment, or against This?
In SwiftUI: Choosing an Application Architecture I ended the article with a list of my criteria for choosing a SwiftUI architecture:
- It should be performant, no matter the size of the application.
- It should be compatible with SwiftUI behavior and state management.
- It should be concise, lightweight, adaptable and flexible.
- It encourages SwiftUI view composition.
- It supports testing.
In other words, it lets SwiftUI be SwiftUI.
Frank Lloyd Wright repeatedly said that architecture should never be independent of its environment.
and I agree. There is a need to select proper architecture, proper tools… for the job at hand.
This can be a bit controversial, so do it.
tell me what you think.