of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
wrapper When you apply a wrapper to a property, the compiler synthesizes code that provides storage for the wrapper and code that provides access to the property through the wrapper. (The property wrapper is responsible for storing the wrapped value, so there’s no synthesized code for that.) You could write code that uses the behavior of a property wrapper, without taking advantage of the special attribute syntax.
code listing that wraps its properties in the TwelveOrLess structure explicitly, instead of writing @TwelveOrLess as an attribute: struct SmallRectangle { private var _height = TwelveOrLess() private var _width = TwelveOrLess() var height: Int { get { return _height.wrappedValue } set { _height.wrappedValue = newValue } } var width: Int { get { return _width.wrappedValue } set { _width.wrappedValue = newValue } } } Where the Compiler is helping From getter / setter to wrapper
_width = TwelveOrLess() var height: Int { get { return _height.wrappedValue } set { _height.wrappedValue = newValue } } var width: Int { get { return _width.wrappedValue } set { _width.wrappedValue = newValue } } } struct SmallRectangle { @TwelveOrLess var height: Int @TwelveOrLess var width: Int }
Int = 1 @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int @SmallNumber(maximum: 9) var width: Int = 2 } @propertyWrapper struct SmallNumber { private var maximum: Int private var number: Int var wrappedValue: Int { get { return number } set { number = min(newValue, maximum) } } init() { maximum = 12 number = 0 } init(wrappedValue: Int) { maximum = 12 number = min(wrappedValue, maximum) } init(wrappedValue: Int, maximum: Int) { self.maximum = maximum number = min(wrappedValue, maximum) } } Setting Initial Values for Wrapped Properties Set wrappedValue in `init`
= SomeStructure() someStructure.someNumber = 4 someStructure.$someNumber @propertyWrapper struct SmallNumber { private var number: Int var projectedValue: Bool init() { self.number = 0 self.projectedValue = false } var wrappedValue: Int { get { return number } set { if newValue > 12 { number = 12 projectedValue = true } else { number = newValue projectedValue = false } } } } Projecting a Value From a Property Wrapper The $var part
read and write a value managed by SwiftUI. SwiftUI manages the storage of any property you declare as a state. When the state value changes, the view invalidates its appearance and recomputes the body. Use the state as the single source of truth for a given view. A State instance isn’t the value itself; it’s a means of reading and writing the value. To access a state’s underlying value, use its variable name, which returns the wrappedValue property value. You should only access a state property from inside the view’s body, or from methods called by it. For this reason, declare your state properties as private, to prevent clients of your view from accessing them. It is safe to mutate state properties from any thread. To pass a state property to another view in the view hierarchy, use the variable name with the $ prefix operator. This retrieves a binding of the state property from its projectedValue property. For example, in the following code example PlayerView passes its state property isPlaying to PlayButton using $isPlaying:
2. ContentView is a struct 3. SwiftUI auto update 4. $text is a Binding struct ContentView: View { @FSState var text = "" var body: some View { VStack { TextField("Write something", text: $text) } } } Cannot assign to property: 'self' is immutable
`self` in the wrapper? import Combine struct WrapperOwner { @Published var i = 0 } 'wrappedValue' is unavailable: @Published is only available on properties of classes
a wrapper type (SE-0258) SE-0258 Property-wrappers 之 Referencing the enclosing 'self' in a wrapper type https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type Instead of a wrappedValue property, a property wrapper type could provide a static subscript(instanceSelf:wrapped:storage:)that receives self as a parameter, along with key paths referencing the original wrapped property and the backing storage property.
a wrapper type (SE-0258) class MyClass: Superclass { @Observable public var myVar: Int = 17 // desugars to... private var _myVar: Observable<Int> = Observable(wrappedValue: 17) var myVar: Int { get { Observable<Int>[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] } set { Observable<Int>[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] = newValue } } } struct SmallRectangle { private var _height = TwelveOrLess() var height: Int { get { return _height.wrappedValue } set { _height.wrappedValue = newValue } } }
The goal of these property wrappers is to avoid implementing a custom init(from decoder: Decoder) throws and suffer through boilerplate. struct Response: Codable { @LossyArray var values: [Int] } let json = #"{ "values": [1, 2, null, 4, 5, null] }"#.data(using: .utf8)! let result = try JSONDecoder().decode(Response.self, from: json) print(result) // [1, 2, 4, 5] 4. BetterCodable