Nested Property Wrappers in Swift
I designed a better way to nest property wrappers than other examples I’ve seen in this article and this Stack Overflow question. The sample below is a redesigned version of the code in the question, which I also posted as an answer. Rather than use multiple property wrappers on the same var, it embeds one inside the other.
import SwiftUI
@propertyWrapper
struct BoundedNumber: DynamicProperty {
private var number: State<Int> // SwiftUI will update when this changes because the struct is a DynamicProperty
private var minimum: Int
private var maximum: Int
init(wrappedValue: Int, minimum: Int, maximum: Int) {
self.minimum = minimum
self.maximum = maximum
number = State<Int>(initialValue: max(minimum, min(wrappedValue, maximum)))
}
var wrappedValue: Int {
get { return number.wrappedValue }
nonmutating set {
number.wrappedValue = max(minimum, min(newValue, maximum))
}
}
var projectedValue: Binding<Int> {
Binding(get: { wrappedValue }, set: { //_ in
wrappedValue = $0
})
}
}
struct BindingTest2: View {
@BoundedNumber(minimum: 0, maximum: 10) var firstNumber = 1
@BoundedNumber(minimum: 1, maximum: 5) var secondNumber = 1
var body: some View {
VStack {
HStack {
Text("\(firstNumber)")
UpdateButton($firstNumber, updateType: .decrement)
UpdateButton($firstNumber, updateType: .increment)
}
HStack {
Text("\(secondNumber)")
UpdateButton($secondNumber, updateType: .decrement)
UpdateButton($secondNumber, updateType: .increment)
}
Button {
firstNumber += 1 // This compiles
} label: {
Image(systemName: "plus")
}
}
}
}
enum UpdateType {
case increment, decrement
}
struct UpdateButton: View {
@Binding var value: Int
let updateType: UpdateType
init(_ value: Binding<Int>, updateType: UpdateType) {
_value = value
self.updateType = updateType
}
var body: some View {
Button {
value += updateType == .increment ? 1 : -1
} label: {
Image(systemName: updateType == .increment ? "plus" : "minus")
}
}
}