With SE-0258, Swift 5.1 adds a new syntactic sugar.
Properties can now add @SomeAwesomeAttribute
, which will wrap the property with some more code.
Should you use it?
Some developers swear never to use didSet
and willSet
on properties, because that’s just adding another place where logics will happen. Another place to look for bug. Another place that can easily break tests..
Similarly, you can avoid using property wrapper. Or at least you can avoid creating your custom ones.
If you use Combine or SwiftUI, then there is no escape from knowing them.
They have good reasons to use it. Some codes can be very repetitive; doing the same thing on a certain property. That’s where a wrapper will make the code much readable.
Example of a custom property wrapper
So if you’re certain a property wrapper will make your code better, here’s how to create one.
@propertyWrapper
struct Loud {
private var original: String
// 1. This is how the wrapper get & set the property
var wrappedValue: String {
get { return original.uppercased() }
set { original = newValue }
}
// 2. An init
init(wrappedValue: String) {
original = wrappedValue
}
// 3. Optional - provide another wrapper value of ANY TYPE, when access via the prefix `$`
var projectedValue: Int {
get { return original.count }
set { original = "\(newValue)" }
}
}
The code creates a @Loud
that can wrap String
properties, making them always UPPERCASED. You can also wrap generics, which I will not go into.
The essence:
@propertyWrapper
declarationwrappedValue
with the type it can wrapinit(wrappedValue:)
with a custom initializationprojectedValue
(optional) is another special wrapper
Usage
Let’s wrap the greetings
property with our custom wrapper @Loud
.
struct User {
@Loud var greetings = "Hello"
}
With that, using the property greetings
will provide the wrapped value.
print(user.greetings) // "HELLO"
You can also use the projected value via $greetings
(prefix with a $
). In our custom property wrapper, it returns the string length, as an Int
.
print(user.$greetings) // 5
A projected value is just another convenient wrapper, for any type.
In Combine, @Published
uses the projected value to access the Publisher
for the property. So $ gives Publisher. It is up to the custom property wrapper on what $ gives.
Since the $
prefix don’t give any meaning on what it actually projects, you have to dig into the code, or RTFD.
So once again, use them sparingly.
BONUS: @dynamicMemberLookup
In WWDC 2019, session 415, along with property wrapper, they also talked about a convenient key path member lookup.
It is nice to understand how the framework provided @State
and @Binding
works.
$slide.title
is slick, because the compiler actually rewrites to $slide[dynamicMember: \Slide.title]
, which is then a Binding<String>
!