For years I've been occasionally dabbling in macOS native application development, and for years I've been resisting the urge to throw my MacBook out the window. Any delusions I've occasionally had about releasing native macOS apps have been flattened by the AppKit pain train.
Where's my Delegate? Or was it the Controller, Model, or View that I needed? Was it in my Storyboard? Xib? Nib? Jib? Why is Xcode crashing? What is this non-deterministic compiler error?
I can see how a dedicated developer could become a weapon with AppKit, but if you're multi-disciplinary the time input/reward ratio is just way off.
Apple has presumably realised this, hence the new kid on the block: SwiftUI. I'd heard about this new-fangled "SwiftUI". It was only this week that my urge to self-flaggellate was strong enough to try it. So I fired up Xcode, and...
Well. What a pleasant surprise. Everything, and I mean the whole damn production, is boiled down into one glorious concept: The View
.
This rustles my jimmies in all the right directions. The only way I ever made any progress with AppKit was to ignore its psychotic smorgasbord of concepts and build self-contained NSView
subclasses. Then, those subclasses could be composed into a UI.
That's effectively SwiftUI in a nutshell. You create View
implementations, and then you add them to each other. That's it.
View
is a protocol, not a class. No inheritance. You have full flexibility to initialise this bad boy however the hell you want. They're just fresh-off-the-stack structs with a .body
property requirement.
Could it get better? Yes it could get better. Some people are imperative people. The want to tell you how to do something, rather than what they want done. These people have titles like "[insert JavaScript framework] Evangelist" and you should socially distance yourself from these people.
Better people are declarative people. They tell you what the objective is, and then leave you alone to achieve it. SwiftUI is a declarative person. The difference in code:
# Imperative Python
relevant_heights: List[int] = list()
for tree in forest:
if tree.age == 4:
relevant_heights.append(tree.height)
return sum(relevant_heights)
# Declarative Python
return sum([t.height for t in forest if t.age == 4])
In the first example, we told Python how to find the right tree heights. In the second example, we told Python what we wanted and let it work out the implementation details itself.
If you've ever worked in SQL, you will be familiar with the declarative style, because SQL is entirely declarative:
/* Declarative SQL */
select sum(height)
from trees
where age == 4
The declarative attitude is the one required to grok the following:
struct Header: View {
var body: some View {
VStack {
Text("Top row text")
Text("Bottom row text")
}
}
}
We declare that we would like a vertical stack of views (VStack
), inside of which we would like two pieces of text (Text
).
When I first saw this, I was frustrated. Where exactly do we return from inside the VStack
? And what is returned? How can two Text
declarations magically turn into two lines of text?
Being an absolute SQL fanatic, I found peace by accepting that we are declaring what we want rather than how we want it done. Writing Swift code against the SwiftUI API is more like writing SQL than say, imperative AppKit code.
We can mix and match views willy-nilly. Say we declare an H1
view, which in our application will be roughly analogous to <h1>
in HTML:
struct H1: View {
let headingText: String
var body: some View {
Text(headingText).font(
.system(
size: 32,
weight: .bold,
design: .default
)
)
}
}
We can now use H1
inside our earlier Header
example:
struct Header: View {
var body: some View {
VStack {
H1(headingText: "Top row text")
Text("Bottom row text")
}
}
}
Producing:
That really is it, kids. Everything else is a variation on the them of defining View
implementations and composing them together. Nested hierarchies of View
become your application user interface.
What a pleasant surprise. SwiftUI might be a keeper. Some quick summary thoughts:
Good
- Everything above
- Xcode doesn't crash constantly anymore
- Swift package manager is great now
The Bad
- Not many built in controls, re-using AppKit classes (e.g. NSTableView) quickly becomes necessary
- AppKit / SwiftUI interaction is poorly documented
- All the Apple docs are (surprise) focused on UIKit people, & iPhone GUI paradigms
Want to dive a little deeper? In a follow up, I discuss how to integrate an AppKit NSTableView into a SwiftUI View hierarchy.
Top comments (0)