Write a @Clamped property wrapper that keeps a value inside a range
Implement a @Clamped property wrapper that constrains a stored value to a closed range passed in the attribute.
Constraints:
- The value set at the declaration site must be clamped too, not only later writes.
- Reading the property returns the clamped value.
- Do not rely on a
didSetobserver.
@propertyWrapper
struct Clamped<Value: Comparable> {
// store the range and the backing value
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
// your code here
}
var wrappedValue: Value {
// your code here
}
}
struct Player {
@Clamped(0...100) var health: Int = 120 // must end up 100
}
Write the implementation.
A @propertyWrapper struct holds the ClosedRange and a backing value; its wrappedValue setter clamps via min(max(...)), and init clamps the first value too. Every write is forced back into the range.
- ✗Forgetting to clamp the initial value passed through
init(wrappedValue:) - ✗Clamping inside a
didSet, which never runs for theinitassignment - ✗Storing the raw value and clamping only when the property is read
- →How would
@Clampedexpose aprojectedValuereachable through$? - →Why must
Valueconform toComparablefor the clamp to compile?
A property wrapper is a @propertyWrapper struct that stores the range and a backing value and clamps it in both init and the wrappedValue setter, so even the initial assignment is constrained:
@propertyWrapper
struct Clamped<Value: Comparable> {
private var value: Value
private let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: Value {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Player {
@Clamped(0...100) var health: Int = 120 // health == 100
}
Clamping in init is essential: a didSet observer does not fire for the initializing assignment, so the constraint logic must live inside the wrapper itself. Value must be Comparable, otherwise min/max will not compile.