Property Wrappers (Part I)

A property wrapper is a mechanism to abstract property implementation patterns that come up repeatedly.

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:

  1. 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.
  2. 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 @propertyWrapperattribute:

Dependency Injection

Inject dependencies via property wrappers.

Reference: https://github.com/ZamzamInc/Shank

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.

Reference: https://github.com/charlieInDen/DataStructuresPractice/blob/master/DataStructuresDemo.playground/Contents.swift

Transforming Values on Property Assignment

Consider the following SpaceRemover struct that removes whitespaces and newlines from incoming string values.

Reference: https://github.com/charlieInDen/DataStructuresPractice/blob/master/DataStructuresDemo.playground/Contents.swift

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. :)