Mission Impossible: A MVC SwiftUI

Ghost protocol poster on IMDB

We proved that this architecture is impossible to be implemented in SwiftUI due to its declarative nature relying totally on value types. If any of you readers can prove I am wrong and there is actually a way of designing an MVC across SwiftUI just comment on this article and I will be happy to learn something new.

A quote from Why the MVC Architecture is Impossible to Implement in SwiftUI?

Well, lucky for him. I happen to be an IMF (Impossible MVC Force) agent.

But why leave a comment that he can delete and block me when I can post an article meme-ing about it?

I remember this guy, because I wrote a code review about his work before.

CoordinatorViewModelPorotocol is the idiotcracy of SwiftUI.

I guess he never got to read to it otherwise I’ll be blocked by now. Usually the larger following a MVVM dev have, the faster he blocks me. I once got blocked for commenting why he thinks it’s OK to have fetch code in view model. That should give you hints on the dog-shit-ness of MVVM with which we are about to uncover.

As the last surviving memeber of IMF, disowned by my agency, I’ll accept this mission impossible.

Let’s use his interpretation of MVC as a starting point.

Note the arrow <- from Model to Controller.

Model is passive, there’s no event to send. If you define an “event” and give it a callback, it becomes a delegate object, which might induce state, which in turn shifts it from value type model object to reference type delegate object.

The simplest you can do with a value type model is to attach a property observer; Model doesn’t know anything about it. This diagram does not consider the distinction between value type and reference type.

For a MVC dev, the diagram can be described as

// set up view delegate, dataSource
// which will mutate model
var model = Model() { didSet {updateUI()}}

Note that this effectively establishes a model -> view binding.

Whenever model changes, view changes.

This binding is important, because SwiftUI introduces model-view binding support. As usual, none of these are shown in the diagram. This is the reason why every MVVM tutorial comes with colorful diagrams yet fail spectacularly.

So what might a MVC SwiftUI look like?

I will say SwiftUI is already MVC; MVC doesn’t mean you must have objects called “controller”. This is a hard concept for MVVM devs to understand, because of their ritualistic worship of the word “view model”; hence their insistance on having an object called view model under any circumstances. If you can accept that you don’t need an object called “controller” to be qualified as MVC, then you can accept that you don’t need an object called “view model” to be qualified as MVVM. Only then can you start to appreciate the fact that you don’t do design pattern for the sake of doing design pattern. You do it for the refactor directions implied by the design pattern.

Note that this dude manually added view model, yet pretended they are built-in by the SDK. The vanilla SDK comes with observable support, namely ObservableObject ; but SDK didn’t require you to put everything in one observable. In fact, SDK wants you to use @State, @EnvironmentObject … etc for different use cases.

Control, is not an object. State can be described by object. State mutation is done by functions. Control is the combination of function and state.

With this in mind, let’s examine vanilla SwiftUI. Where’s control?

Do you have state? Yes. Marked by the big red keyword @State.

Do you have functions, or state mutations? Of course.

Then you have MVC!

Control is not an object

MVC represents a refactor. View is special enough, you want to refactor it out. Use model as state, then build control via functions.

Obviously by this definition, everything will be MVC. Well, yes, that’s why it’s a fundamental design pattern. Does MVVM not have Control? Of course it has. It hides under view model. Over time MVVM devs just treat Control as "view model", an object, which is later refactored out to be “interactor”, still an object.

But where’s the fun in that? Let’s say you need a Controller object.

Since SwiftUI has binding support. I’m going to use binding support. There’s no rule that binding as a language feature is exclusive to MVVM, right?

class ViewController {
var model = Model()
}

Now we need to handle UI events from view and update view from model changes.

Let’s say this view controller has views A, B. In the same way we set tableView.delegate = viewController , we set the delegate in A, B like

struct A: View {
@EnvironmentObject var vc: ViewController
}

where ViewController instance is created and owned by some top level object and is passed down as an environment object. I don’t want to waste time creating initializers and pass it down level by level when you have SDK support to simplify the process.

But since A is a value type, that means viewController cannot have a reference to this view! It can only have a copy!

This is where publisher-observer pattern kicks in. SDK only allows you to publish stuff to whoever observes it. So you can’t abuse a view reference; you can only communicate using a pre-defined path, without knowing who is observing. So view controller becomes

class ViewController: ObservableObject {
@Published var model = Model()
}

You have no choice. No plan, no back up, no choice.

Yet … mission accomplished.

We’ve setup view event observing and @Published doubles as view updater and property observer. This is the diagram right here in case you forgot:

So what’s the problem? Seems like a mission plausible to me.

Let’s compare what we’ve done so far to that of … well, Russian prison.

He is trying to replicate a viewController in UIKit to SwiftUI.

He uses Binding to get around value type, which should raise red flag.

And since ContentViewis value type, view is a local copy. This binding won’t update the actual “view”.

You can predict his conclusions miles away just from this snippet:

As you can see, the two labels are not being updated and there is a simple reason for that: The views we are “referencing” inside our Controller are not reference types

Duh. Because you ignore mechanisms of SwiftUI to make a UIKit view controller. Technically he isn’t wrong in this statement, except possibly the fact that he passes .constant as binding in

which would make this binding… constant. Double whammy. You would think better programming should have an editor or something so they don’t publish something with this kind of mistakes.

That which is wrong is what he draws from this statement.

We proved that this architecture is impossible to be implemented in SwiftUI due to its declarative nature relying totally on value types.

Well, I just did it. Super Easy, Barely an Inconvenience.

Now it’s my turn to draw some conclusions.

In my design, views A, B share a controller.

If we make a special case, and let each view has its own view controller. Instead of @EnvironmentObject, we use @ObservedObject; and we put everything in respective view controller for no reason. Guess what we have?

MVVM.

This is what happens when you rely on colorful diagrams without SDK context, instead of building around SDK features.

This also echoes the previou discussion that SwiftUI is MVC, with or without controller. In fact, you should notice the similarities between what I do with property observer and @Published in SwiftUI. SwiftUI is a syntax sugar of the same process in UIKit (Swift, not Objc).

Remember the discussion that for a MVX pattern, you don’t actually need objects M, V, and X?

SwiftUI is a working example. You can have functionalities of a view model without ritualistically creating a view model for every view. SDK gives you better flexibility, the same way you can have functionalities of a view controller without creating a view controller for every view. It generalizes and extends beyond these concepts over a decade long of experiences. MVVM is not new. I’ve seen the same shit a decade ago. After a language change followed by a SDK re-design from scratch, don’t you think it’s time for some new shit?

Perhaps the most ironic part of all. MVVM in SwiftUI does exactly like what you do with a massive view controller: create a sink object, and dump everything to it, for every view. I write better MVVM codes without view model, and MVVM devs write dog shit MVC codes disguised as view model.

Speaking of dog shit, let’s look at an example.

This is dog shit in MVC standards. (he did it in MVVM too)

build is global, you can call it from anywhere. It may start as a refactor since the setup is too tedious, which should raise all red flags. That means you risk over-generalization or breaking of encapsulation.

But being a MVVM dev, design pattern >>>> refactor. If you pay attention, this is actually GMVC. G stands for Global, or God. So named because there’s a global scope, or God, governing the creation of MVC. ContentController here is being controlled! By God! It doesn’t control shit!

Everything has to be injected, nobody has knowledge of anything. So God has to orchestrate everything. The real controller is build , a function! Over-generalization is dog shit. There’s no reason for him to do this because there are no type-erase protocols, which is dog shit too because most of the time you are not going to pass in another type except mocking, which is dog shit too because mocking should be designed as light-weight as possible not something you have to declare a whole set of types that require further generalization of production code on top of being orchestrated by God.

In this case controller is local, but somehow its view got returned. So why do you need this controller ? Either this is not production code or there could be side effects from controller.view = view via property observer. This is some dog shit. No caller other than him knows how to initialize these shit properly.

I could go on, but I think I’ve made my point. There are more than 100 claps for this article, which I hope they are all from his friends, and are not from serious iOS developers.

Oh shit, speaking of which. There’s a VIP in comment section with over 10K followers and decades of experiences. Quote:

You're not wrong, but you went an awfully long way to get there.

Bottom line is that MVC-style delegation won't work in SwiftUI not because "views" are not reference types, but because in SwiftUI "views" are not views in the way we used to think of them in UIKit

Obviously he is wrong for saying “you’re not wrong”. But his reasoning seems right, while my arguments seem right too. So who is right?

We could both be right. Because I didn’t use UIKit-style delegation. I use SwiftUI-style delegation. Like I said, I’ll use binding support by SDK. They are there for a reason.

I don’t use the word MVC-style delegation becasue what? You don’t need MVC-style delegation for MVVM in UIKit which has no binding support? MVC can’t use KVO in UIKit?

This is what I called a MVVM echo chamber, which might explain why this shit could be recoginized as “you are not wrong” by industry titans.

This is why MVVM is stagnant. You can get away with Order(n) solution where n is the number of views; when the point of refactor is to design something of Order(1) so you don’t have to write the same shit every time.

Oh btw, as far as I know, the VIP still thinks struct SomeView: View is a view rather than struct Model: View as a model-view binding. Who is right? You decide.

If you think I’m right, then the two of us and the contents of this car are all that remains of the Impossible MVC Force.

Your mission … should you choose to accept it?

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store