Alongside many new feature that come with the Swift 5.1, one of the most interesting feature is Property wrappers. Basically, it’s a layer/delegate that sits in the middle between how the declared property should behave and how the property will be stored. Property wrappers can be defined by using
enum. It can be used when we declare properties inside of those types also.
Swift has already provided several built in wrappers before Swift 5.1 such as
@NSCopying, but with the introduction of Property Wrappers, developer can now also implement custom wrappers without making the language become more complex. You can read the reasoning behind this proposal (SE-258) in the Swift Evolution link below.
There are property implementation patterns that come up repeatedly. Rather than hardcode a fixed set of patterns into…
Property Wrappers is also heavily used in SwiftUI. There are many wrappers provided by the framework such as:
@State. A property wrapper which value binds to the view where it’s declared in.
@Binding. It’s a property that is passed down from the view parent’s
@ObservedObject. Similar like
@State, but used for a property which conforms to
ObservableObjectneeds to be a
classtype and will update the View whenever the properties that are marked with
@Published. It’s a wrapper that can be used for properties that are declared in
ObservableObject. Whenever the value changes, it will invoke
objectWillChangemethod so View can react to the changes published.
@EnvironmentObject. Similar like
@ObservedObject, but it can be used to share data across many views from top to bottom of view hierarchy without passing down the property explicitly to child view.
@Environment. It is used to inject and override system wide configuration such as system color scheme, layout direction, content size category into a View.
Property wrappers is not exclusively limited to SwiftUI, with Swift 5.1 we can create our own custom property wrapper!. Here are some of the things we can do by creating our own custom wrappers:
- Transforming a value after it’s being assigned.
- Limiting the value to minimum and maximum bounds.
- Provide extra projected value on a property.
- A Wrapper that act as delegate to hide implementation details of an API.
Those are just small examples of the wrappers that we can create, the possibilities ahead are endless!. Next, let’s implement some of those property wrappers and see how we can use them to simplify our code!.
You can download sample SwiftUI application of using the custom property wrappers that we are going to create. Check the project GitHub repository below.
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
Using Property Wrapper in a nutshell
Creating a new property wrapper it’s pretty simple. Here are some of the steps to create it:
@propertyWrapperkeyword before we declare the type that we want to use as property wrapper. It can be
- We are required to implement the
wrappedValueproperty. Most of the time we declare custom
getterin this property. This property can be a
- Initializer will pass the
wrappedValuewhen we assigning the property a value when we declare it. We can also create our own custom initializer with additional properties. We’ll see more on this later in the examples of
- We can also declare an optional
projectedValueproperty of any type. This can be accessed using
$from the property.
- To use it we simply put the property wrapper with
@as the prefix when we declare the property in our type.
Next, let’s begin implementing custom property wrappers!.
Transforming value of a property
@Uppercased wrapper, we want to make sure the
String is uppercased anytime a value is assigned into the property. Here are the things that we do to implement this:
- We store the actual string inside a private stored property named text.
- The required wrappedValue property is a computed property, whenever we assign a value, it will be stored in text property and whenever we get the property, text value will be returned by applying uppercased method.
- We create a wrappedValue initializer and then assign it to text property the first time the wrapper is initialized.
- To use it, we just add the
@Uppercasedkeyword in front of the property.
Limit the minimum and maximum bounds of a number value
@Ranged wrapper can be used to clamp value of number by providing maximum and minimum value. Whenever the value is assigned, comparison will be performed and value will be assigned based on these conditions:
- If new value assigned is larger than maximum bounds, the maximum value will be used to store the property.
- If new value assigned is smaller than minimum bounds, the minimum value will be used to store the property.
- If both of these conditions are not met, new value will be used to store the property.
To accept minimum and maximum parameter, a custom initializer is created. When we declare the property, we also need to pass the maximum and minimum value after the
Project Date property to ISO8601 formatted String
Property wrappers can also be used to project another value of any type using the
projectedValue property, it can be accessed using the
$ operator by prefixing the property. For
ISO8601DateFormatter , a static
ISO8601DateFormatter is used whenever
projectedValue is read to convert the date from the
wrappedValue stored property.
Wrapping NSLocalizedString API with property wrappers
@Localizable property wrapper is used to wrap the
NSLocalizedString API, when a property declared using
@Localizable keyword, the value assigned will be stored in the private
key property and will be used whenever the
wrappedValue is accessed by passing it to
NSLocalizedString(key:comment:) initializer to get the localized string from the app.
Wrapping UserDefaults API with property wrapper
UserDefaults API can be very cumbersome for us, everytime we want to persist and retrieve value from user defaults. We can simplify this by creating a simple property wrapper that will hide the implementation of those API calls for whenever we assign and retrieve value from a property.
@UserDefault wrapper accepts 2 parameter in the initializer, the
initialValue in case of the value for the key is not available in
UserDefaults . The
wrappedValue itself is a computer property, whenever a value is assigned, it will set the value using the
key stored. And whenever the property is read, the
key is used to retrieve the value as cast it using
generic . If the value is not available, the
initialValue will be returned instead.
Property Wrappers is a really amazing feature that we can use to provide custom shared patterns and behaviour in properties that we declare in our type to simplify our code. I really hope in the future there will be many great wrappers created and shared by the community.
At last, keep on learning and evolving to become a more better coder to build insanely great things through technology. Let’s keep the lifelong learning goes on and happy Swifting!.