Start Coding

Topics

Swift Property Wrappers

Property wrappers are a powerful feature introduced in Swift 5.1 that allow you to add custom behavior to properties with minimal boilerplate code. They provide a way to encapsulate property storage and define reusable getter and setter logic.

What are Property Wrappers?

Property wrappers are structs, enums, or classes that encapsulate read and write access to a property. They enable you to abstract away common property patterns, making your code more modular and easier to maintain.

Basic Syntax

To create a property wrapper, use the @propertyWrapper attribute before the type definition:

@propertyWrapper
struct Capitalized {
    private var value: String = ""
    
    var wrappedValue: String {
        get { value }
        set { value = newValue.capitalized }
    }
}

To use a property wrapper, simply add the wrapper's name with an @ symbol before the property declaration:

struct User {
    @Capitalized var name: String
}

var user = User()
user.name = "john doe"
print(user.name) // Output: "John Doe"

Common Use Cases

  • Value validation
  • Type conversion
  • Access control
  • Lazy initialization
  • Thread safety

Advanced Features

Projected Values

Property wrappers can expose additional functionality through projected values, accessed using the $ prefix:

@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    private let range: ClosedRange<T>

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }

    var projectedValue: ClosedRange<T> { range }

    init(wrappedValue: T, _ range: ClosedRange<T>) {
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
        self.range = range
    }
}

struct Temperature {
    @Clamped(0...100) var celsius: Double
}

var temp = Temperature(celsius: 25)
print(temp.celsius) // Output: 25.0
print(temp.$celsius) // Output: 0.0...100.0

Property Wrapper Composition

You can combine multiple property wrappers to create more complex behaviors:

@propertyWrapper
struct Trimmed {
    private(set) var value: String = ""

    var wrappedValue: String {
        get { value }
        set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
    }

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

struct User {
    @Trimmed @Capitalized var name: String
}

var user = User(name: "  john doe  ")
print(user.name) // Output: "John Doe"

Best Practices

  • Use property wrappers to encapsulate reusable property logic
  • Keep property wrappers simple and focused on a single responsibility
  • Consider performance implications, especially for frequently accessed properties
  • Use projected values to expose additional functionality when necessary
  • Combine property wrappers with Swift Protocols for more flexible designs

Related Concepts

To deepen your understanding of Swift property wrappers, explore these related topics:

By mastering property wrappers, you'll be able to write more concise, reusable, and maintainable Swift code. They are particularly useful when working with Swift Structures and Swift Classes, allowing you to add powerful behaviors to your properties with minimal effort.