Skip to main content
A property wrapper is a compiler-synthesized mechanism that introduces a layer of separation between the code that manages how a property is stored and the code that defines the property. By applying the @propertyWrapper attribute to a custom type, you define a reusable encapsulation strategy for property access and mutation. To create a property wrapper, a struct, class, or enum must be annotated with the @propertyWrapper attribute and must implement a non-static property named wrappedValue.

Compiler Desugaring

When you apply a property wrapper to a declaration, the Swift compiler automatically generates a backing storage variable and routes all access to the original property through the wrapper’s wrappedValue.
Under the hood, when applied to a property on a type, the compiler desugars the declaration into the following equivalent code:
When a property wrapper is applied to a local variable within a function, the compiler synthesizes the same backing storage and computed access, but access control modifiers (like private) are omitted since they do not apply to local scopes.

Initialization Semantics

Property wrappers support implicit initialization at the call site if the wrapper type defines an init(wrappedValue:) initializer. When a default value is assigned to the wrapped property, the compiler translates this assignment into a call to this specific initializer.
Wrappers can also define custom initializers to accept additional arguments. These are passed in the attribute declaration syntax.

Projected Values

A property wrapper can optionally expose a secondary API by defining a projectedValue property. The compiler synthesizes access to this projected value by prefixing the original property name with a dollar sign ($). The projectedValue can return any type, including self (the wrapper instance itself), allowing external code to interact with the wrapper’s internal state or auxiliary methods.

Architectural Constraints

  • Protocols: Property wrappers cannot be applied to property requirements inside a protocol declaration (e.g., protocol P { @Wrapper var x: Int { get } } is invalid).
  • Property Modifiers: A property wrapper cannot be applied to lazy, weak, unowned, or computed properties.
  • Access Control:
    • Backing Storage: When applied to a type’s property, the synthesized backing storage (_propertyName) is always private. When applied to a local variable, access control modifiers do not apply.
    • Wrapped and Projected Values: The synthesized properties (propertyName and $propertyName) share the access level of the original property declaration. Consequently, the wrapper’s underlying wrappedValue and projectedValue implementations must be at least as accessible as the wrapped property. If a property is declared public but the wrapper’s projectedValue is internal, the compiler does not silently downgrade the projection’s access level; instead, it emits a compile-time error.
Tired of Poor Swift Skills? Fix That With Deep Grasping!Learn More