Property Wrappers (Part I)
Property Wrappers (Part I)
A property wrapper is a mechanism to abstract property implementation patterns that come up repeatedly.
We can solve any problem by introducing an extra level of indirection.
Imagine you have an object’s property and you want to apply some additional functionality when the property’s value is being set or accessed. Something like lazy loading, some data modifications or additional actions. Most of these things are something that you would usually do in property’s accessors (set/get, didSet/didGet, willSet/willGet). Before we go into details of property wrappers lets read about decorator pattern, extensions, subclassing, we already used these concepts at any point of programming.
Decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C.
Subclassing is the act of basing a new class on an existing class. The subclass inherits characteristics from the existing class, which you can then refine. You can also add new characteristics to the subclass.
From the swift programming language book , swift 5.1
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
Lets try to understand the property wrapper!!
Property wrappers abstract these actions from the specific data
types and make it applicable to the other data models. You
basically take the code to implement it and move it to a
separate place which makes possible to reuse it. Property
wrappers can also be treated as a implementation of decorator
pattern or way to way to add some functionality without changing
the object’s original implementation like extensions or
subsclassing.
Property wrapper
adds new behaviour to a specific instances of the data
structure. It doesn’t influence all the other instances of
this type (in a contract to the extension or subclassing). So changing
the functionality of just one property, one instance of some
data structure you shouldn’t worry about any side effects for
the others.
There are two basic requirements for a property wrapper type:
-
The property wrapper type must be defined with the attribute
@propertyWrapper
. The attribute indicates that the type is meant to be used as a property wrapper type, and provides a point at which the compiler can verify any other consistency rules. -
The property wrapper type must have a property named
wrappedValue
, whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the wrapper instance.
So, for your consideration, here are some potential use cases
for the new
@propertyWrapper
attribute:
Dependency Injection
Inject dependencies via property wrappers.
For more details, please visit Dependency Inject using Property Wrappers.
Constraining Values
Consider a use case where you want to add the minimum and maximum text limit on a text field in iOS application, by creating a @Limit property wrapper, you can directly add the limit to a variable.
Transforming Values on Property Assignment
Consider the following
SpaceRemover
struct that removes whitespaces and newlines from incoming
string values.
Copy-on-write
With some work, property wrappers can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon):
protocol Copyable: AnyObject {
func copy() -> Self
}
@propertyWrapper
struct CopyOnWrite<Value: Copyable> {
init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}
private(set) var wrappedValue: Value
var projectedValue: Value {
mutating get {
if !isKnownUniquelyReferenced(&wrappedValue) {
wrappedValue = value.copy()
}
return wrappedValue
}
set {
wrappedValue = newValue
}
}
}
Combine’s Published
property wrapper is similar in spirit, allowing clients to
subscribe to
@Published
properties (via the
$
projection)
to receive updates when the value changes.
SwiftUI
makes extensive use of property wrappers to declare local state
(@State
) and
express data dependencies on other state that can effect the UI
(@EnvironmentObject
,
@Environment
,
@ObservedObject
). It makes extensive use of projections to the
Binding
property wrapper to allow controlled mutation of the state that
affects UI.
Hope this article is useful for iOS developers, Please ❤️ to recommend this post to others 😊. Let me know your feedback. :)