SwiftUI vs. the Reactive MVVM iOS Architecture

Kevin Tan
Kevin Tan
Aug 14 · 12 min read

Overview

Apple’s introduction of SwiftUI, alongside the rising popularity of Flutter, Jetpack Compose, and React/React Native(particularly with hooks), is an indicator of the future of front-end development: a functional and declarative style of programming supplanting the traditional stateful and object-oriented one.

Image for post
Image for post
Figure 1: State-chan and View-kun
class Observable<T> {
// Array of callbacks with a single generic parameter
private let subscribeBlocks = Array<T -> Void>()

func subscribe(block: T -> Void) {
self.subscribeBlocks.append(block)
}

/// Notify all observers
func notify(with event: T) {
for block in subscribeBlocks {
block(T) // Execute the callback
}
}
}
// Suppose we have an observable bound to a UISwitch and we are
// broadcasting the event the user switches it on or off.
let switchObservable = Observable<Bool>()
// Subscribing looks like…
switchObservable.subscribe { isOn in
if isOn {
updateViewSomeWay()
} else {
updateViewSomeOtherWay()
}
}
// Notifying looks like…
switchObservable.notify(true)
Image for post
Image for post
Figure 2: Ugly MVVM diagram courtesy of Xamarin

Enter SwiftUI

During WWDC 2019, Apple announced the development of a framework introducing a completely new way of building iOS apps: SwiftUI. It leverages many powerful features of Swift such as protocols, function builders, and implicit returns to facilitate a declarative style of building user interfaces, similar to how HTML looks. In many ways, it seems to mimic React; here’s a fun diagram I made loosely mapping React concepts to their SwiftUI counterparts:

Image for post
Image for post
Figure 3: Figma is objectively the best design software.

Current iOS SDK

import UIKitclass HelloWorldViewController: UIViewController {
let label = UILabel()

override func viewDidLoad() {
super.viewDidLoad()

label.text = “Hello, world!”
view.addSubview(label)
}

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

var labelFrame = CGRect.zero
labelFrame.size = label.intrinsicContentSize
labelFrame.origin.x = (view.bounds.width — labelFrame.width) / 2
labelFrame.origin.y = (view.bounds.height — labelFrame.height) / 2

label.frame = labelFrame
}
}

SwiftUI

import SwiftUIstruct HelloWorldView: View {
var body: some View {
Text(“Hello, world!”)
}
}
  1. SwiftUI is not a JavaScript framework.
  2. SwiftUI is superior to React.

RxCatFact

I stumbled upon the fun cat fact API at https://catfact.ninja a while ago, but never had the opportunity to build a toy app around it. I started by whipping up a quick Figma prototype:

Image for post
Image for post
Figure 4: Still unconvinced Figma is the best?
  • The cat emoji to change to another random cat emoji.
  • A call to the cat fact API, with the fetched fact displayed in the center box. While we wait for the network call to complete, the contents of the box are replaced with a loading indicator.
Image for post
Image for post
Figure 5: I’m running out of witty captions.

Conclusion: Annoying vs Good

I kept a list of things I found both annoying and good while I worked on this, and rather than list each point in detail I’ve grouped them to form overarching themes and concepts I’d like to discuss. I’ll start with the annoying bits so we can end on a good note. 😄😄

Annoying

1. SwiftUI is still too young.

Any new framework is bound to have its share of kinks and flaws, and SwiftUI is not exempt. While building this app, I ran into many minor inconveniences, the kind that give rise to thoughts like “Why?” and “ew Gross” (with a capital G) when you eventually fix them. They include:

  • Absolute positioning can be difficult.
  • You can’t declare constants in Function Builders.
  • The EnvironmentObject check happens at run-time and can crash your app.
  • Transition animations only work on View add/removal.

An unnecessary technicality: Modifier order matters.

One of the most notable ways SwiftUI exhibits its functional style is through the way you modify a view’s properties. For example, consider the following code snippet:

// SwiftUI
Rectangle()
.foregroundColor(.blue)
.cornerRadius(10)
.opacity(0.5)
// UIKit
let view = UIView()
view.backgroundColor = .blue
view.layer.cornerRadius = 10
view.alpha = 0.5
VStack {
Button(“Hello, world!”) { }
.frame(width: 100, height: 100)
.background(Color.red)
Button(“Hello, world!”) { }
.background(Color.red)
.frame(width: 100, height: 100)
}
Image for post
Image for post
Figure 6: but why tho

An ugly workaround: Transition animations only work on view add/removal.

SwiftUI has greatly improved the ease of adding animations to views. In the following snippet, the excerpts of code do roughly the same thing:

// SwiftUI
Rectangle()
.scaleEffect(isLarge ? 1.5 : 1)
.animation(.easeInOut)
// UIKit
let animations: () -> Void
if isLarge {
animations = {
viewToAnimate.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}
} else {
animations = {
viewToAnimate.transform = .identity
}
}
UIView.animate(
duration: 0.5,
delay: 0.0,
options: .curveEaseInOut,
animations: animations,
completion: nil
)
UIView.transition(
with: viewToTransition,
duration: 0.5,
options: [.curveEaseInOut, .transitionCrossDissolve],
animations: {
viewToTransition.backgroundColor = .red
},
completion: nil
)
GradientBackground(colors: outputs.backgroundColors)
.edgesIgnoringSafeArea(.top)
.transition(.opacity)
.id(outputs.backgroundColors.hashValue)

2. SwiftUI abstracts a lot of iOS development away.

This second and final annoying point comes from my perspective as someone that has helped teach iOS development to new computer science students for four years. As much as I hate storyboards and Auto-layout, I always start my lessons there because it’s simple, easy to use, intuitive, and allows you to get something working in a matter of minutes. But at least with storyboards, there is a foundation upon which you can segue (ha ha) into fundamental iOS concepts such as the View Controller lifecycle, the view hierarchy, the protocol-delegate pattern, etc.

Good

1. Easy to extend and interoperate with UIKit.

Onto the good parts! This first one is fairly short and sweet: it’s pretty easy to create adapters for UIKit components that you can add to your SwiftUI views. This is primarily achieved through the UIViewRepresentable and UIViewControllerRepresentable protocols. You can take a look at ActivityIndicator.swift and ActivityViewController.swift in the repo to see how this works. While Apple continues to work on porting UIKit views to SwiftUI, these protocols serve as a pretty good alternative in the meantime.

2. Easily facilitates a powerful architecture and well-organized codebase.

One of the most notorious questions for any iOS developer is: “What architecture should I use?” Apple’s answer to that is MVC, which stands for Model-View-Controller. The idea is to separate raw data (models) from the actual user-facing views. The two interact through the controller object, which is responsible for reading and updating models and telling views what to display. The problem that arises from this architecture is also called MVC, or Massive View Controller. Since the view controllers have so much work to handle, you end up shoving all of your logic (business, data formatting, view updating) inside them and end up with these files that are thousands of lines long. Not very scalable or easy to work with.

3. It’s Swift.

Pun intended. I want this point to end my post, because it really is the most promising thing I see about SwiftUI. The iOS development process is far from perfect. It has taken me an extremely long time (4+ years) to become very comfortable with UIKit and the intricate processes that underlie any iOS app.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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