Trusted answers to developer questions
Trusted Answers to Developer Questions

Related Tags

swift
communitycreator

How to use property wrappers in Swift

Michael Verges

Introduction

Property wrappers are introduced in Swift 5.1 as a mechanism to define common behavior patterns.

Property wrappers can reduce boilerplate code by defining accessorsgetters and setters for an underlying storage type, known as the wrappedValue.

How to declare a property wrapper

Property wrappers are defined by structs, classes, or enums prefixed with the @propertyWrapper attribute.

Property wrappers must contain a wrappedValue field that provides the interface to get and set the value.

@propertyWrapper
struct MyWrapper {
  var wrappedValue: Int
}

Call site syntax

Unlike other initializers, property wrappers are instantiated with a unique syntax.

@MyWrapper var myVar = 0

Lead initializer parameters

Property wrappers can define initializers with any parameters. These parameters are entered at the call site.

Here is an initializer with parameters:

@propertyWrapper
struct MyWrapper {

  init(name: String) {
    wrappedValue = 0
  }
  
  var wrappedValue: Int
}

A call site may look as follows:

@MyWrapper(name: "hello") var myVar

Note: wrappedValue is defined within the initializer, so we do not explicitly set an initial value.

wrappedValue initializer parameters

Any initializers that end with a wrappedValue parameter are not instantiated at the call site. These parameters are instead defined on the right-hand side of the assignment declaration.

This initializer has a trailing wrappedValue:

@propertyWrapper
struct MyWrapper {
  
  init(name: String, wrappedValue: Int) {
    self.wrappedValue = wrappedValue
  }

  var wrappedValue: Int
}

The call site may look like so:

@MyWrapper(name: "hello") var myVar = 0

Code

Example 1

Uppercase.swift creates a property wrapper for strings. The wrapper defines a setter that stores an uppercase version of the value.

import Foundation

/// Always returns an uppercased version of
/// the wrapped string.
@propertyWrapper 
public struct Uppercase {
  
  public init(wrappedValue: String) {
    /// Store the uppercased string
    self.value = wrappedValue.uppercased()
  }
  
  public var wrappedValue: String {
    /// Return the stored string
    get {
      return value
    }
    
    /// Store the uppercased string
    set {
      value = newValue.uppercased()
    }
  }
  
  private var value: String
}

struct Main {
  @Uppercase var myString = "Hello, world!"
}

print(Main().myString) // "HELLO, WORLD!"
Uppercase Property Wrapper

Example 2

Consumable.swift creates a property wrapper for any generic value. The wrapper defines a setter that stores the value. The getter returns a value if one exists, and also removes (or “consumes”) the stored value.

import Foundation

/// Creates a value that can only be accessed once.
/// Accessing the value will consume and clear the value.
@propertyWrapper
enum Consumable<Value> {
  case unused(Value?)
  case used
  
  init(wrappedValue: Value?) {
    self = .unused(wrappedValue)
  }
  
  var wrappedValue: Value? {
    mutating get {
      switch self {
      case .unused(let value):
        self = .used
        return value
      case .used:
        return .none
      }
    }
    set {
      self = .unused(newValue)
    }
  }
}

struct Main {
  @Consumable var myValue = 1
}

var main = Main()

// The first getter consumes the value
print(main.myValue as Any) // 1
print(main.myValue as Any) // nil

// Set the consumable to a new value
main.myValue = 2

// The first getter consumes the value
print(main.myValue as Any) // 2
print(main.myValue as Any) // nil

RELATED TAGS

swift
communitycreator
RELATED COURSES

View all Courses

Keep Exploring