The most important goal as an API designer: Clarity at the point of use.
@DynamicMemberLookup
It can be used to expose all the properties of a hidden object as if they belonged to the main object.
@dynamicMemberLookup
struct Material {
public var roughness: Float
public var color: Color
private var _texture: Texture
public subscript<T>(
dynamicMember keyPath: ReferenceWritableKeyPath<Texture, T>
) -> T {
get { _texture[keyPath: keyPath] }
set {
if !isKnownUniquelyReferenced(&_texture) {
_texture = Texture (copying: _texture)
}
_texture[keyPath: keyPath] = newValue
}
}
}@PropertyWrapper
Provides similar benefits to the built-in lazy
Eliminates boilerplate.
Documents semantics at the point of definition.
Property Wrappers can be seen as generic getters and setters:
// Implementing a Property Wrapper
@propertyWrapper
public struct LateInitialized<Value> {
private var storage: Value?
public init() {
storage = nil
}
public var value: Value {
get {
guard let value = storage else {
fatalError("value has not yet been set!")
}
return value
}
set {
storage = newValue
}
}
}// Uses of property wrappers expand into a stored property and a computed property public
public struct MyType {
@LateInitialized public var text: String
}
// Compiler-synthesized code 👇🏻
public struct MyType {
var $text: LateInitialized<String> = LateInitialized<String>()
public var text: String {
get { $text. value }
set { $text. value = newValue }
}
}We can even pass values to the property initializer:
// Defensive Copying
@propertyWrapper
public struct DefensiveCopying<Value: NSCopying> {
private var storage: Value
public init(initialValue value: Value) {
storage = value. copy () as! Value
}
public var value: Value {
get { storage }
set {
storage = newValue.copy() as! Value
}
}
}// Initializing the backing storage property:
public struct MyType {
@DefensiveCopying(withoutCopying: UIBezierPath())
public var path: UIBezierPath
}