Automatic handling of property wrapper default values
Basics article available: PropertiesSwift’s property wrappers feature enables us to encapsulate a given property value in order to run custom logic whenever that value was created or changed. For example, the following Capitalized
type (which was borrowed from my full-length article on property wrappers) lets us automatically capitalize all String
values that were assigned to any of our wrapped properties:
@propertyWrapper struct Capitalized {
var wrappedValue: String {
didSet { wrappedValue = wrappedValue.capitalized }
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.capitalized
}
}
One really neat aspect of property wrappers is that the compiler will automatically map any default value that we’ve defined at the call site to the above init(wrappedValue:)
initializer — meaning that we can keep defining default values just as we normally would, even when a property is wrapped by one of our custom types:
struct Document {
@Capitalized var name = "Untitled document"
}
That automatic default value mapping even extends to property wrappers that accept additional initializer parameters as well. For example, here we’ve defined a property wrapper that lets us override a given value using a command line argument, and simply by naming our default value parameter wrappedValue
(and placing it first within our initializer argument list), we’ll be able to keep assigning default values to the properties that are being wrapped:
@propertyWrapper struct CommandLineOverridable {
let wrappedValue: Bool
var flagName: String
private let defaults = UserDefaults.standard
init(wrappedValue defaultValue: Bool, flagName: String) {
self.flagName = flagName
#if DEBUG
// First, we check if a command line argument matching
// our flagName even exists, before retrieving a Bool
// value for it, since otherwise we'd get 'false' back
// for flags that weren't passed at all:
if defaults.object(forKey: flagName) != nil {
wrappedValue = defaults.bool(forKey: flagName)
return
}
#endif
wrappedValue = defaultValue
}
}
Note how we’re using Foundation’s UserDefaults
API to parse command line arguments above. To learn more about that, check out “Launch arguments in Swift”.
With the above in place, we now just have to tell each CommandLineOverridable
instance what flag that we want to use for each given property, and the compiler will automatically map that property’s default value to our wrappedValue
initializer argument:
struct AppConfiguration {
@CommandLineOverridable(flagName: "sync")
static var enableSync = true
@CommandLineOverridable(flagName: "rememberLogin")
static var rememberLogin = false
}
To learn more about property wrappers in general, check out my full-length article “Property wrappers in Swift”, which goes into a lot more detail.
Thanks for reading!