Fernando Olivares

August 9, 2023

Understanding SwiftUI property wrappers

I just finished Chapter 3 of Thinking in SwiftUI and I need to wrap my brain around property wrappers.

For one, I find it funny how Objective-C'y it feels to have a property wrapper. This pattern of synthetization and property declaration is very similar to what we had in Objective-C.

I think I need to summarize what each property wrapper does, regardless if iOS 17 changes things. I'm only catching up to iOS 14 😉. So let's give it a go.

If a struct that conforms to View requires data, the best thing is to provide no dynamism at the subview level. A constant injected at initialization is the fastest way of making sure your view is rendered only once.

However, that's obviously not always possible. In these cases, 
  • Providing the data as var is not possible since we're dealing with structures. Yes, in theory, we could mutate the variable but how would SwiftUI know of this mutation since it itself is holding a copy of the original structure?
  • Enter @State. @State will magically hold whatever you give it and "pair" it with the view. This means we can get the process of mutating state and view with SwiftUI's blessing. @State is commonly associated with private values, meaning they're inherently owned by whoever declared the @State property.
  • Then there is @StateObject which is the same as @State but holds a reference to an object. This should also be marked private, since it's owned by the subview.
  • If we do not own the data, we should use @Binding. @Binding is a two-way street for value-based properties and changes to them. Just like @State is magically held wherever SwiftUI thinks best, so is @Binding. @Binding allows the data to be read/mutated by both ends of the binding.
  • Obviously, for reference-based properties, we should then use @ObservedObject.

The biggest drawback of this reactive system is that you have to be careful to encapsulate as much data as possible. If you held a struct or an object and something in it changes, it's unlikely you'd want the full UI to be redrawn. Each view should hold as little data as possible, and should own as much of it as possible so you don't end up with redrawing loops.

That seems about right, right? 😉

Let's see what else I can learn in the next few chapters.