Property Wrappers in Swift

Darshana Jaisingh
3 min readMay 5, 2024

--

Photo by Denis Pavlovic on Unsplash

Property wrappers are a feature introduced in Swift to provide a convenient way to add custom behavior to properties. They enable developers to encapsulate common property patterns and behaviors into reusable components.

Let's understand the above with examples:

Before Property Wrappers:

Before property wrappers, achieving custom behavior for properties often involved writing repetitive boilerplate code within the property’s getter and setter methods.

Let’s consider an example where we want to ensure that a property always contains a non-empty string.

class User {
private var _name: String = "Default"

var name: String {
get {
return _name
}
set {
if !newValue.isEmpty {
_name = newValue
}
}
}
}

In this example, we have a User class with a name property. To ensure that the name property is always set to a non-empty string, we need to include validation logic within both the getter and setter methods. This results in boilerplate code that needs to be repeated for each property with similar behavior.

After Property Wrappers:

With property wrappers, we can encapsulate this common behavior into a reusable component, making the code more concise and maintainable.

@propertyWrapper 
struct NonEmpty {
private var value: String

var wrappedValue: String {
get { value }
set {
if !newValue.isEmpty {
value = newValue
}
}
}

init(wrappedValue: String) {
if !wrappedValue.isEmpty {
self.value = wrappedValue
} else {
self.value = "Default"
}
}
}
class User {
@NonEmpty var name: String
}

In this example, we define a NonEmpty property wrapper that ensures the wrapped property (name) always contains a non-empty string. By applying the @NonEmpty property wrapper to the name property, we eliminate the need to write validation logic within the User class. This results in cleaner and more readable code, with the behavior encapsulated within the property wrapper.

Additionally, property wrappers promote code reuse, as the NonEmpty wrapper can be applied to other properties with similar requirements.

Let’s consider another example where the property wrapper performs some simple logic, such as converting a string to uppercase before storing it.

@propertyWrapper
struct Uppercased {
private var value: String

var wrappedValue: String {
get { value }
set { value = newValue.uppercased() }
}

init(wrappedValue: String) {
self.value = wrappedValue.uppercased()
}
}

struct User {
@Uppercased var name: String
}

let user = User(name: "John")
print(user.name) // Output: "JOHN"

In this example, we define a Uppercased property wrapper that ensures any string assigned to the wrapped property (name) is converted to uppercase.

  • In the init(wrappedValue:) initializer, we convert the initial value to uppercase and store it.
  • In the wrappedValue getter, we simply return the stored value.
  • In the wrappedValue setter, we convert the new value to uppercase before storing it.

When we create an instance of User and initialize it with the name "John", the Uppercased property wrapper automatically converts the name to uppercase. Subsequent accesses to the name property will always return the uppercase version of the name, ensuring consistency in the data stored.

Real-World Examples:

Property wrappers can be used in various real-world scenarios to simplify data management and enforce certain constraints. Some examples include:

  • Validating input data: Ensuring that user-provided input meets certain criteria (e.g., minimum length, valid format).
  • Caching computed properties: Storing the result of expensive computations to improve performance.
  • Implementing lazy initialization: Delaying the initialization of properties until they are accessed for the first time.

Limitations:

While property wrappers provide a convenient way to add custom behavior to properties, they have certain limitations. For example, property wrappers cannot be applied to local variables within functions, as they are designed to wrap stored properties of types. Additionally, the behavior of property wrappers is limited to the scope in which they are defined, such as within a class or struct.

Overall, property wrappers simplify the process of adding custom behavior to properties, reducing boilerplate code and improving code maintainability.

Happy Coding!

--

--

Darshana Jaisingh

I'm on a journey to master iOS/macOS development. Let's code together, explore new ideas, and grow together in the world of Apple development!