Attaching property wrappers to function arguments
Basics article available: PropertiesNew in Swift 5.5: Property wrappers can now be applied directly to function arguments, just like how they can be used to add additional functionality to a property or local variable.
For example, let’s say that an app that we’re working on contains a function that saves a Photo
for a given name, and that we always want to normalize each name by lowercasing it. One way to do that would be to override the passed name
argument using a local property — like this:
func savePhoto(_ photo: Photo, named name: String) {
let name = name.lowercased()
...
}
There’s certainly nothing wrong with the above approach, but let’s take a look at how we could now use a property wrapper to instead embed our lowercasing transformation into the function argument itself.
To get started, let’s create a Lowercased
property wrapper, which automatically lowercases any String
value that was assigned to it:
@propertyWrapper struct Lowercased {
var wrappedValue: String {
didSet {
wrappedValue = wrappedValue.lowercased()
}
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.lowercased()
}
}
Then, we can now attach our new Lowercased
wrapper directly to our name
argument, which will ensure that its value will always be lowercased, without requiring that transformation to happen inline within our function’s main body:
func savePhoto(_ photo: Photo, @Lowercased named name: String) {
...
}
Pretty neat! Even property wrappers that accept arguments of their own can be attached using the above technique. For example, the following Truncated
wrapper automatically truncates its wrapped collection so that it only contains a certain number of elements:
@propertyWrapper struct Truncated<Value: RangeReplaceableCollection> {
var wrappedValue: Value {
didSet { wrappedValue = Value(wrappedValue.prefix(maxLength)) }
}
var maxLength: Int
init(wrappedValue: Value, maxLength: Int) {
self.wrappedValue = Value(wrappedValue.prefix(maxLength))
self.maxLength = maxLength
}
}
With the above in place, we can now easily truncate any collection-based function argument simply by attaching our new Truncated
wrapper to it — like this:
func updateFavorites(@Truncated(maxLength: 20) to favorites: [Item]) {
...
}
While this new feature doesn’t really provide us with any revolutionary new ways to write Swift code, it does add an extra degree of consistency to Swift’s overall property wrappers system — since wrappers can now be applied to properties, local variables, and function arguments, all with the same kind of functionality.