0
0
Swiftprogramming~7 mins

Composing property wrappers in Swift

Choose your learning style9 modes available
Introduction

Composing property wrappers lets you combine multiple behaviors on one property easily. It helps keep code clean and reusable.

You want to add validation and logging to a property at the same time.
You need to apply caching and thread safety together on a variable.
You want to reuse common property behaviors in different parts of your app.
You want to separate concerns by stacking small wrappers instead of one big wrapper.
Syntax
Swift
@Wrapper2 @Wrapper1 var propertyName: Type
Property wrappers are applied from right to left, so @Wrapper1 wraps the property first, then @Wrapper2 wraps the result.
Each wrapper must have an initializer compatible with the wrapped property.
Examples
This property is first clamped between 0 and 10, then logged when changed.
Swift
@Logged @Clamped(min: 0, max: 10) var score: Int = 5
The name is trimmed of spaces and saved in user defaults.
Swift
@UserDefault("username") @Trimmed var name: String = ""
Sample Program

This program shows two property wrappers combined. The health property is clamped between 0 and 100, and changes are logged.

Swift
import Foundation

@propertyWrapper
struct Clamped {
    private var value: Int
    let min: Int
    let max: Int

    var wrappedValue: Int {
        get { value }
        set { value = Swift.min(Swift.max(newValue, min), max) }
    }

    init(wrappedValue: Int, min: Int, max: Int) {
        self.min = min
        self.max = max
        self.value = Swift.min(Swift.max(wrappedValue, min), max)
    }
}

@propertyWrapper
struct Logged {
    private var storage: Clamped

    var wrappedValue: Int {
        get { storage.wrappedValue }
        set {
            let oldValue = storage.wrappedValue
            storage.wrappedValue = newValue
            print("Changing value from \(oldValue) to \(storage.wrappedValue)")
        }
    }

    init(wrappedValue: Int) {
        self.storage = Clamped(wrappedValue: wrappedValue, min: 0, max: 100)
    }

    init(wrappedValue: Clamped) {
        self.storage = wrappedValue
    }
}

struct Player {
    @Logged @Clamped(min: 0, max: 100) var health: Int = 50
}

var player = Player()
player.health = 120
player.health = -10
player.health = 80
OutputSuccess
Important Notes

Order matters: the wrapper closest to the property applies first.

Each wrapper can add its own logic like validation, logging, or storage.

Composing wrappers keeps code modular and easier to maintain.

Summary

Composing property wrappers means stacking multiple wrappers on one property.

They apply behaviors in order, from right to left.

This technique helps add multiple features cleanly and reuse code.