Swift 5.4 is now released and includes language improvements and tooling improvements. Paul Hudson, as usual, has a great summary of what is new hosted at https://www.whatsnewinswift.com.

Property wrappers which which first appeared in Swift 5.1, abstract away access patterns on a type continue to improve in Swift 5.4 by allowing them to be be applied to local variables. The easiest way to see what they do is to look at the API.

@propertyWrapper
struct <#Name#> {
    private var value: <#Wrapped#>
    var wrappedValue: <#Wrapped#> {
        get { value }
        set { value = newValue }
    }
    var projectedValue: <#Projected#> {
        get { value }
        set { value = newValue }
    }
    init(wrappedValue: <#Wrapped#>) {
        value = wrappedValue
    }
}

You use this type with the @ mark to give types the special behavior. @Behavior var velocity: Double. The wrappedValue is used where you use velocity and you can get at the projectedValue with $velocity.

The only real requirement is that you implement wrappedValue. It is not required to have a projected value. For example:

@propertyWrapper
struct Clamping<T: Comparable> {
  init(wrappedValue: T, range: ClosedRange<T>) {
    self.value = range.clamp(wrappedValue)
    self.range = range
  }

  private var value: T
  let range: ClosedRange<T>
  var wrappedValue: T {
    get { value }
    set { value = range.clamp(newValue) }
  }
}

extension ClosedRange {
  func clamp(_ value: Bound) -> Bound {
    Swift.min(Swift.max(value, self.lowerBound), self.upperBound)
  }
}

class Spaceship {
  @Clamping(range: 1...100) var velocity: Double = 10
}

let t = Spaceship()
t.velocity           // 10
t.velocity = 25
t.velocity           // 25
t.velocity = 200
t.velocity           // 100

This doesn’t have a projected value. You might add one that keeps the original value passed in. With that design way you could project to the unclamped original, which might be useful.