This week we are giving a sneak peek into what our next major series of episodes will be on Point-Free, and it’s free for everyone to watch! We will be discussing how to build modern UIKit features, with an eye on domain modeling, bindings, and navigation!
Watch now!
It may seem a little strange for us to devote time to “modern UIKit”. After all, isn’t SwiftUI all the rage? Well, as much as we want our apps to be 100% SwiftUI, there are going to be times we need to drop down to UIKit. It could be due to lack of functionality in SwiftUI, or perhaps certain tools in UIKit are more performant (UICollectionView
😍).
And so once you have started writing your first UIViewController
subclass in ages, the question becomes: what is the most modern way to do it? SwiftUI completely revolutionized how we think about building apps for Apple’s platform, but its powers can be broken down into roughly two categories:
SwiftUI provides a lightweight way to build view hierarchies using value types,
and SwiftUI provides powerful state management tools that keep models in sync with what is visually on screen.
The former is not something we will be discussing. There are libraries out there that aim to provide a nice interface to UIKit components, but we are going to let UIKit be UIKit when it comes to building interfaces.
However, the latter, in particular state management, can be completely revolutionized when it comes to UIKit. Thanks to Swift’s powerful new observation tools we can bind models to UIKit controllers and views in a very succinct syntax. And we can even drive navigation from state using APIs that look similar to SwiftUI.
It is actually pretty incredible to see!
In our series, “modern UIKit” refers to the style of building UIKit apps with concise and powerful state management tools inspired by SwiftUI. In SwiftUI, one can model the domain of their feature in an observable object like so:
@Observable
class CounterFeature {
var count = 0
var fact: Fact?
var isLoadingFact = false
}
…and then construct a simple view hierarchy that accesses whatever state from the model
that is needed:
Form {
Text("\(model.count)")
Button("Decrement") { model.count -= 1 }
Button("Increment") { model.count += 1 }
if model.isLoadingFact {
ProgressView()
}
}
.disabled(model.isLoadingFact)
.sheet(item: $model.fact) { fact in
Text(fact.value)
}
…and SwiftUI has the awesome ability to observe the minimal amount of state for the view (i.e. only the fields accessed on model
), and drive navigation from state (i.e. sheet is presented when fact
is non-nil
and dismissed when nil
).
What if we had the ability to minimally observe the model in UIKit in order to update UI controls? And what if we could drive the presentation of view controllers purely from state? And further, what if we could do all of this in a short, concise syntax like this:
func viewDidLoad() {
super.viewDidLoad()
// Set up view hierarchy…
observe { [weak self] in
guard let self else { return }
countLabel.text = "\(model.count)"
decrementButton.isEnabled = !model.isLoadingFact
incrementButton.isEnabled = !model.isLoadingFact
activityIndicator.isHidden = !model.isLoadingFact
}
present(item: $model.fact) { fact in
FactViewController(fact: fact)
}
}
The observe
tool would observe only the model
fields accessed in the trailing closure, and would automatically be invoked when those fields change. And the present(item:)
function would observe changes to fact
in order to present the FactViewController
when the state becomes non-nil
, and dismiss when it becomes nil
. And further, if the user dismisses the controller by swiping down, then the model.fact
state will automatically be nil
’d out.
All of this is absolutely possible, and this is what we call modern UIKit.
While we are primarily talking about UIKit in this series, don’t miss an opportunity to read between the lines. In this series we are really showing how one can build the logic and behavior of their app without ever thinking about view-related concerns. Your first priority in building your app should be in concisely modeling your domain.
With that done you can let the view flow from the domain. Then it doesn’t matter what view paradigm you use. You are free to use your models in either UIKit or SwiftUI because none of the view-specific concepts infiltrated your domain.
But even more interesting, you are also free to use your domain models in other platforms. Cross platform Swift is becoming more popular these days, with efforts to bring Swift applications to Windows, Linux, and even the web using Wasm. Our explorations into modern UIKit development are a mere shadow of what is possible when porting an application to other platforms.
There’s no better time to learn about modern UIKit. We will show how a few simple tools built on Swift’s Observation framework allows one to model domains concisely and describe complex navigation patterns in just a few lines of code.
Watch now!