Swift Property Wrappers

From now on with each new version of Swift that comes out I’m going to take some time a dig through all the Swift Evolution Proposals that made it into that release. Some real gems made it into the last few releases that I didn’t know anything about but are really cool. The one I’m talking about today is a feature called Property Wrappers. Property Wrappers actually made it into Swift 5.1 all the way back in September of 2019. (We’re currently at 5.3.) The absolutely tragic thing to me is that none of the books I’ve read that supposedly covered Swift 5.1 even talk about this despite the feature solving a whole host of chicken/egg problems among others.

Property Wrappers are implemented as just syntactic sugar in the compiler. Think of it as a macro that the compiler replaces with something else much the same way that the Java compiler takes the following source code:

String str = anotherString + " and more text.";

and replaces is on the fly with:

String str = anotherString.concat(" and more text.");

In the case of Property Wrappers what happens is that you can now define a code block that gets executed any time the value of a property is set or retrieved. Now you might think that this is the same thing as the willSet or didSet property observers but it’s not. For one thing you cannot use willSet or didSet to alter the value that’s being set. Also willSet and didSet have no analog for when the property value is retrieved.

For example, one thing you can do is use Property Wrappers to define a string property that automatically converts any string assigned to it to uppercase. You start out be defining a new Property Wrapper like so:

@propertyWrapper
public struct UpperCased {
  private var value: String = ""
  public var wrappedValue: String {
    get { value }
    set { value = newValue.uppercased() }
  }
  public init(wrappedValue: String) {
    self.wrappedValue = wrappedValue
  }
}

And then, any property you want to use this Property Wrapper gets declared like so:

@UpperCased var upperCaseString: String = "Some string that will be uppercased."

Then what happens is the compiler will automatically replace the code above, at compile time, with the following.

var _upperCaseString: UpperCased(wrappedValue: "Some string that will be uppercased.")
var upperCaseString: String {
  get { _upperCaseString.wrappedValue }
  set { _upperCaseString.wrappedValue = newValue }
}

Now, some people might look at this and think, “So what?” But what Property Wrappers do is to allow much cleaner, easier to read code while, at the same time, promoting code reuse. The example I have here is pretty basic and almost pointless. But you could have wrappers that are very complex and/or operate on very complex values. Then, once coded, those wrappers can be used over and over again. Not to mention that, in the example above, having @UpperCased before the declaration makes it very clear to everyone that that value is going to be UPPERCASED!

You can read the very in-depth proposal here: Property Wrappers

Leave a Reply