BLOGS
Read Only Property Wrappers in Swift
Property Wrappers are one of my favorite additions to Swift 5.1. If you haven’t read about them before, check out the proposal and this NSHipster article. They are very similar to Kotlin’s delegated properties, but the syntax is similar to Java’s annotations.
It may not be readily apparent, but one really nice feature of Property Wrappers is you can create “read only” property wrappers. These are properties that you can read from, but not write.
What would be a real life example of this? An example of this use might be a globally accessible constant key-value store. One problem with globally accessible stores is that you don’t want the underlying data modified. Ideally you would only allow it to be set once:
This could be handled with a singleton and a non-reentrant function.
|
|
We only allow starting this function once. This works fine:
|
|
But If I try to call start multiple times:
|
|
I get an error that it’s already started:
error: alreadyStarted
We can then declare a property wrapper for this data store:
|
|
What’s interesting here is that I’m not declaring the setter for the wrapped value. Only the getter. I can even remove the get
block because swift implicitly interprets a block definition as a get
. This makes a very nice and concise property wrapper:
|
|
Here is how to use the property wrapper in another class:
|
|
This will get the value which was previously set globally. If the value was not set, then it’ll just return nil
.
What’s more interesting is that I can’t modify the variable, even though it’s declared as a var. This is because the property wrapper has no setter method defined, only a getter. If I try to set it, I’ll get a compiler error:
|
|
This gives us a nice clean interface to define a global key-value store that can only be set once, but referenced throughout the app. The property wrapper gives is easy access to getting value, visibility to where the value is coming from, and prevents us from misusing our interface.
It’s even safer than you think!
What happens if I declare the property as a non-optional?
|
|
I get a compiler error telling me that I’m using an optional type that needs to be unwrapped. Complete with a fixit to unwrap it:
Even though I declared my type as a non-optional, the type returned is an optional. This is because I’m forcing the wrapped value to always return an optional. Command-clicking on the variable confirms this:
Final Thoughts
Property wrappers are a relatively simple construct, but digging into them leads to some pretty safe and intuitive behavior. While a pretty and powerful safe abstraction, one danger with this syntactic abstraction is over-using them to the point where it becomes hard to reason about code (custom operators are another example of this). All property wrappers should be well documented and include any caveats. Finally, if the property isn’t being stored on the class itself (like in my example), it should be clearly stated where the object is stored.