tezvyn:

Swift Property Wrappers: Reusable Logic for Properties

AI-drafted, machine-checkedSource: github.comintermediate

A Swift Property Wrapper is a template for a property's get/set logic, letting you reuse behaviors like data validation without boilerplate. It powers SwiftUI's @State and is great for managing UserDefaults.

WHY IT EXISTS Many properties share common implementation patterns, like being lazy, thread-safe, or persisted to user defaults. Before property wrappers, Swift hardcoded a few of these (like lazy), but developers had to write repetitive boilerplate for their own custom patterns. Property wrappers provide a general, reusable way to define this logic once and apply it anywhere.

THE MENTAL MODEL A Property Wrapper is a separate type that manages a property's storage and access. Think of it as delegating a property's implementation. When you write @UserDefault(key: "hasSeenOnboarding") var hasSeenOnboarding: Bool, you're telling the compiler to use the UserDefault wrapper's logic to handle getting and setting the hasSeenOnboarding boolean, instead of just storing it in memory.

HOW IT WORKS A type becomes a property wrapper by being marked with the @propertyWrapper attribute and implementing a wrappedValue property. The compiler then rewrites access to the property you've decorated. A call to myProperty is translated into a call to _myProperty.wrappedValue, where _myProperty is the hidden, compiler-synthesized instance of the wrapper type. A wrapper can also provide a projectedValue, accessed with a dollar sign prefix (e.g., $myProperty), to expose additional functionality, like the binding from a SwiftUI @State property.

WHEN TO USE IT Use property wrappers to eliminate boilerplate for recurring property logic. Three key places this shines: first, managing state, which is the foundation of SwiftUI (@State, @Binding, @Environment); second, abstracting storage, like reading/writing from UserDefaults or a database; and third, enforcing constraints, such as clamping a number within a range or ensuring a string is always lowercase.

WHEN NOT TO USE IT Avoid property wrappers for logic that is unique to a single property, as the added abstraction isn't worth the complexity. If the logic is simple and doesn't need to be reused, a standard computed property with a getter and setter is often clearer. For simple stored values with no special behavior, a wrapper is just unnecessary overhead.

ONE CANONICAL EXAMPLE SwiftUI's @State is a property wrapper. When you declare @State private var tapCount = 0, you're not just creating an integer. You're creating an instance of the State struct, which holds your integer. The wrappedValue gives you direct access to the tapCount integer. The projectedValue (accessed via $tapCount) gives you a Binding, which you can pass to child views so they can also read and write the value, triggering UI updates automatically.

Read the original → github.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.