And the first look at NotificationCenterPublisher property wrapper
As the name suggests, a property wrapper is a layer that wraps around your usual property to provide additional functionality. This layer acts as a middle man between you and the actual property. So, whenever you want to set or get a value of a property, you have to communicate through the property wrapper. Now, you may ask “Why introduce a middle man in this interaction?”. Keep reading and you will find your answer.
Let’s say you are a developer on the Twitter application and you want to store a new tweet in a property. As you may know, a tweet is limited to only 280 characters. Ideally, you already have a UI that restricts the user from entering more than 280 characters.
But, that doesn’t mean that we have to limit this character check only to the UI. What if the UI breaks and suddenly people are posting essays on Twitter. You don’t want to be fired right? Let’s see how a property wrapper can save your job.
In the above example, we use a property called
tweet to store our actual tweet. If you notice, just before our variable declaration, we have
@RestrictedCharacterCount(maxCharacterCount: 33). That’s our property wrapper.
To use a property wrapper, start with the
@ symbol followed by the property wrapper name. We also pass in 33 as our
maxCharacterCount value since that will be the maximum number of characters that we will be storing for this example.
From the output, you can observe that only the first 33 characters of our sentence that we tweeted, was actually stored on our property. The rest was ignored. If you remember, this is because we explicitly set the “maxCharacterCount” to 33 earlier. Now, you can set this number to 280 characters and keep your job at twitter.
You might also have noticed that we acesss our property wrapper object using the $ (dollar) sign before our property name. That’s called as the projected value.
Before we celebrate our win, let’s understand how this works underneath the hood.
To write a property wrapper,
- You can use either a struct or a class
- Annotate your object with
- Declare a property called
wrappedValue. It has to be non-static. This is the actual property that we will be wrapping up. The data type of this property is up to you.
- You can either give your
wrappedValue, a default value, or let the user provide it. To do the latter, make sure to include
wrappedValueas part of your object’s initializer.
- Apart from receiving the value for your
wrappedValuein your initializer, you can also specify other parameters that you feel are required for your property wrapper.
- Now, like a middle man, whenever the property is “set” or “get”, you can freely change the value according to your logic. What you decide will become the final value that is stored or returned to the user.
- The projected value is something which the user can access when they prefix the property name with a $ (dollar sign). What you return as the projected value is completely up to you.
- For example, you might have a few methods or properties on your property wrapper to provide extra functionality to the user. To expose this, you can return the property wrapper itself as the projected value.
Now that you have a basic understanding of how things work, let’s look at the implementation for our property wrapper
@RestrictedCharacterCount from earlier.
“Bro, why you gotta make things complex by making it generic”
Bear with me for a few minutes. Let me break this down for you and you will realize how really powerful this is. So, here’s the core idea.
Make a property wrapper that will take in a maximum character count as a parameter and whenever the character count of the data stored inside the property, goes beyond the maximum character count, truncate the extra characters, from the end. The property wrapper should also be compatible with a wide range of data types to allow for maximum flexibility
Now, let’s break down our property wrapper,
- To make it compatible with a wide range of data types, we make the data type of our
wrappedValuegeneric and call it T.
- We also make sure that T is something that conforms to the
LosslessStringConvertibleprotocol. This allows for free conversion between T ←→ String.
- Now, we convert our
wrappedValueof data type T, to String to count the number of characters. If we exceed the
maxCharacterCount, we trim the extra characters at the end. Finally, we convert this trimmed string back to its original data type T and store it inside the
- So, anytime the user sets a value for our property, we examine the data, count the characters, trim any extra characters and then finally store it.
Here are some examples of why making it generic made it really powerful:
Let me give you a few more examples to solidify your understanding.
@RestrictedRange is a property wrapper that helps you restrict the value of your property, to a specific range. Whenever any user tries to set a value that does not fall within the valid range, a closure is called to replace it with a valid value.
@NotificationCenterPublisher is a generic property wrapper that publishes every new value of the property using Notification Center.
You must pass the Notification name and can also, optionally set a closure called
getUserInfo which will allow you to pass in the
userInfo dictionary, just before the notification is posted.
In summary, property wrappers help you manipulate the value of a property, everytime it is read or written by the user. This access logic once writtern in the form of a property wrapper, can be reused and applied to any property in your codebase.
You can optionally add a
projectedValueto your property wrapper, which can then be accessed through the fancy $ (dollar) syntax. This is useful for exposing any addtional functionality in your property wrapper.
Property wrappers are a big part of the SwiftUI world and understanding them is crucial.