Property Wrapper 入門

Rx を使ってプロパティの変更通知をしたい、だけどクラス外からは値の変更を許したくないというユースケースがよくあると思います。

そうしたとき、以前までは以下のように書いていました。

```swift
struct Person {
  private let _age: BehaviorSubject<Int>
  var age: Observable<Int> { _age.asObservable() } 
  
  init(initialAge: Int) {
    _age = BehaviorSubject(initialAge)
  }
}
```

しかし、変更通知をしたいだけなのにわざわざこんなに書くのは冗長極まりないです(個人の感想です)。

そこで、 Swift 5.1 で追加された新たな機能、 **Property Wrappers** の出番です。

最初に以下のようなオブジェクトを用意します。

```swift
@propertyWrapper
struct PropertyPublished<Value> {
  private(set) var wrappedValue: Value {
    didSet {
      subject.onNext(wrappedValue)
    }
  }
  
  var projectedValue: Observable<Value> {
      subject.asObservable()
  }
  
  private let subject: BehaviorSubject<Value>
  
  init(wrappedValue: Value) {
      self.wrappedValue = wrappedValue
      subject = BehaviorSubject(value: wrappedValue)
  }
  
  mutating func onNext(_ newValue: Value) {
      wrappedValue = newValue
  }
}
```

あとは変更通知したいプロパティの前に `@PropertyPublished` をつけるだけです。

```swift
struct Person {
  @PropertyPublished var age: Int
  
  init(initialAge: Int) {
    _age = PropertyPublished(wrappedValue: initialAge)
  }
  
  mutating func increment() {
    _age.onNext(age + 1)
  }
}

// 使い方
var person = Person(initialAge: 10)
person.$age.subscribe(onNext: { print("Age: \($0)") })
print(person.age)
person.increment()

// Output:
//   Age: 10
//   10
//   Age: 11
```

ずいぶんすっきりしました。

---

さて、すごくシンプルに書けたのはいいのですが、上の例で Swift 5.1 未満では謎な文法がありました。

`Person` 内ででてきた `_age` は `Person` では明示的に定義していません。
使うときに出てきた `person.$age` も `Person` では明示的に定義してません。
また、`PropertyPublished` 内で定義した `wrappedValue` や `projectedValue` は他の名前では代替できません。

これらは全て Property Wrapper の機能として組み込まれており、とても柔軟で便利な機能を実現することができます。

ここでは Property Wrapper の基本的な動作について、見ていきます。

## 基本
Property Wrapper の最低限の用件は以下です。

- Property Wrapper 型にしたい型に `@propertyWrapper` をつける
- Property Wrapper 型は `wrappedValue` というインスタンスプロパティを持つ。

これだけです。

実際に作っていきましょう。
まず Property Wrapper 型にしたい型に `@propertyWrapper` をつけます。

こうすることでコンパイラーに「この型は Property Wrapper 型です」ということを伝えます。

```swift
@propertyWrapper
struct PropertyPublished {
}
```

この時点でコンパイルすると、

```
Property wrapper type 'PropertyPublished' does not contain a non-static property named 'wrappedValue'
```

と怒られます。
Property Wrapper 型は `wrappedValue` というプロパティを持つことが最低条件でした。
なので `wrappedValue` を追加します。

```swift
@propertyWrapper
struct PropertyPublished<Value> {
  var wrappedValue: Value
}
```

これで最低条件は満たせました。
実際に使ってみましょう。
使い方も簡単でプロパティの前に `@{Property Wrapper 型の名前}` をつけるだけです。

```swift
struct Person {
  @PropertyPublished(wrappedValue: 10) var age: Int
}
```

こうすることで上のコードをコンパイラーは以下のように展開します。

```swift
struct Person {
  private var _age: PropertyPublished<Int> = PropertyPublished(wrappedValue: 10)
  var age: Int {
    get { _age.wrappedValue }
    set { _age.wrappedValue = newValue }
  }
}
```

まさに Property を Wrap してますね！

ちなみに `@{Property Wrapper 型の名前}` の後に `()` をつけることでその Property Wrapper 型のイニシャライザを呼び出すことができるのですが、 `.init(wrappedValue:)` の呼び出しだけは糖衣構文として以下が用意されています。

```swift
@PropertyPublished var age: Int = 10
// ↑ は ↓ と等価
// @PropertyPublished(wrappedValue: 10) var age: Int
```

とてもシンプルですね！

ただこのままではプロパティの変更を検知できません。
なので Observable を外に見える形で公開しなければなりません。

ここで Property Wrapper で便利な機能である `projectedValue` の出番です。

以下のように `projectedValue: Observable<Value>` なプロパティを追加します。

```swift
@propertyWrapper
struct PropertyPublished<Value> {
  var wrappedValue: Value

  var projectedValue: Observable<Value> {
    subject.asObservable()
  }
  
  private let subject: BehaviorSubject<Value>
  
  init(wrappedValue: Value) {
    self.wrappedValue = wrappedValue
    self.subject = BehaviorSubject(value: wrappedValue)
  }
}
```

値の更新を購読可能にするために `BehaviorSubject` も一緒に追加しました。
そして `projectedValue` はただ `BehaviorSubject` を `Observable` として公開しているだけです。

こうすることにより、

```swift
struct Person {
  @PropertyPublished(wrappedValue: 10) var age: Int
}
```

は以下のように展開されます。

```swift
struct Person {
  private var _age: PropertyPublished<Int> = PropertyPublished(wrappedValue: 10)
  
  var $age: Observable<Value> {
    _age.projectedValue
  }
  
  var age: Int {
    get { _age.wrappedValue }
    set { _age.wrappedValue = newValue }
  }
}
```

`$age` という変数が追加されました。
また、このときのプロパティの可視性は `age` と同じく internal です。
これにより、もともとラップしたプロパティの型は変えずに新たにプロパティの型を外に公開できます。

また、今回は `projectedValue` には getter しか定義しませんでした。
当たり前と言えば当たり前ですが、 `wrappedValue` や `projectedValue` が get-only で setter がない場合や、 `private(set)` などで setter が外部に公開されていない場合は同じく展開されたプロパティにも setter は生えません。

上記までの実装で必要なプロパティは揃えることができました。
あとは実際に新しい値がセットされたとき、`PropertyPublished` の `subject` を発火させてあげればいいだけです。

ということで発火させる用のメソッドを `PropertyPublished` に追加してあげます。


```swift
@propertyWrapper
struct PropertyPublished<Value> {
  private(set) var wrappedValue: Value {
    didSet {
      subject.onNext(wrappedValue)
    }
  }

  var projectedValue: Observable<Value> {
    subject.asObservable()
  }
  
  private let subject: BehaviorSubject<Value>
  
  init(wrappedValue: Value) {
    self.wrappedValue = wrappedValue
    self.subject = BehaviorSubject(value: wrappedValue)
  }
  
  func onNext(_ newValue: Value) {
    self.wrappedValue = newValue
  }
}
```

変更通知を飛ばすのは `onNext` メソッドを通して欲しいため `wrappedValue` を `private(set)` にしています。

これにより `age` が get-only になりました。

また、 最初から自動で生成されていた `_age` は private な変数なのでもちろん `Person` 内からならアクセスできます。

```swift
struct Person {
  @PropertyPublished var age: Int = 10
  
  mutating func increment() {
    _age.onNext(age + 1)
  }
}
```

この `Person.age` を監視する方法は以下です。

```swift
let person = Person()
person.$age.subscribe(onNext: { /* 処理 */})
```

ずいぶん短いコードで

- 本来見せたいプロパティの値である `Value`
- 外部からは見える `Observable<Value>`
- 外部からは見えない `Subject<Value>`

を達成することができました！

Swift 5.0 以前でこれを実装しようとすると以下のように書かなければなりません。

```swift
struct Person {
  private let _age: BehaviorSubject<Int>
  // `$` はユーザー定義変数では使えない
  var observableAge: Observable<Int> { _age.asObservable() } 
  var age: Int = 10 {
    didSet {
      _age.onNext(age)
    }
  }
  
  init() {
   _age = BehaviorSubject(value: age)
  }
  
  mutating func increment() {
    age = age + 1
  }
}
```

長いですね。
また、これはプロパティが増えるごとに追加しなければなりません。
Property Wrappers を使えばただ `@PropertyPublished` を追加していけばいいだけです。

```swift
struct Person {
  @PropertyPublished var age: Int
  @PropertyPublished var name: String
  
  init(age: Int, name: String) {
    self._age = PropertyPublished(wrappedValue: age)
    self._name = PropertyPublished(wrappedValue: name)
  }
}
```

シンプルでいいですね！

ただ、ちょっと残念なのが、

```swift
self.age = age
```

と書けずに

```swift
self._age = PropertyPublished(wrappedValue: age)
```

になっている点です。

[Property Wrapper の Proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#out-of-line-initialization-of-properties-with-wrappers) には

```swift
@Lazy var x: Int
// ...
x = 17   // okay, treated as _x = .init(wrappedValue: 17)
```

とあるので、 init 内ならいけないかなーと思ったのですが、

```
Cannot assign to property: 'age' is a get-only property
```

でコンパイルエラーになるのでダメみたいですね。。。
なので大人しく直接 `_age` を初期化しています。

ただ、前までのように大量にプロパティを追加していく必要もなく、同じような処理を何度も書かなくていいので非常に便利です。

## 制限
Property Wrapper はとても便利なのですが、便利が故にいろいろ制限があります。

- ラップしたプロパティは protocol で使用できません
- ラップしたインスタンスプロパティを extension では宣言できません
- ラップしたインスタンスプロパティを Enum では宣言できません
- class 内で宣言されたラップしたプロパティは override できません
- ラップしたプロパティには `lazy`、 `@NSCopying`、 `@NSManaged`、 `weak`、 `unowned` をつけられません
- ラップしたプロパティはそれを囲む宣言内で唯一のプロパティでなければなりません
  - `@PropertyPublished var (x, y) = ...` は不正
- ラップしたプロパティは getter と setter を持てません
- Property Wrapper 型と wrappedValue は、同じアクセス権を持ちます
  - PropertyPublished を public にしたなら wrappedValue も public にしないとダメ
- init(wrappedValue:) を宣言した場合、Property Wrapper 型と init(wrappedValue:) は、同じアクセス権を持ちます
- projectedValue を宣言した場合、Property Wrapper 型と projectedValue は、同じアクセス権を持ちます
- init() を宣言した場合、Property Wrapper 型と init() は、同じアクセス権を持ちます

## 将来追加される(と思われる)機能
Property Wrappers は今でも結構便利ですが、まだまだフル機能ではありません。
Proposal に将来実装される(予定)機能があるので紹介します。

### より細かいアクセス制御
現状、`_age` と `$age` のアクセス権は固定になっており、制御できません(`_age` は private、 `$age` はラップしたプロパティと同じ)。

なのでこれらも使う側で制御できるようにしようという提案があります。

現状考えられているのは以下のような構文。

```swift
@PropertyPublished
public internal(storage) private(projection) var foo: Int = 0
```

上のように書くと、以下のように展開される予定です。

```swift
internal var _foo: PropertyPublished<Int> = PropertyPublished(wrappedValue: 0)
private var $foo: PropertyPublished<Int> { _foo.projectedValue }
public var foo: Int { _foo.wrappedValue }
```

便利なのですが、初見では困惑しそうですね。

### ラップされたプロパティを持つインスタンスを参照する
現状の Property Wrapper 型はラップされたプロパティを所持しているインスタンスにアクセスできません。

なので現状手動で PropertyWrapper 型へ伝える必要があります。

```swift
protocol Observed {
  func broadcastValueWillChange<Value>(newValue: Value)
}

@propertyWrapper
struct BroadcastObservable<Value: Equatable> {
  private var stored: Value

  var wrappedValue: Value {
    get { stored }
    set {
      if newValue != stored {
        observed?.broadcastValueWillChange(newValue: newValue)
      }
      
      stored = newValue
    }
  }

  private var observed: Observed? = nil
  
  init(wrappedValue: Value) {
    self.stored = wrappedValue
  }
  
  mutating func register(_ observed: Observed) {
    self.observed = observed
  }
}

// 今回は話の都合上 class になります
class Person: Observed {
  @BroadcastObservable var age: Int = 0
  
  init() {
    _age.register(self)
  }
  
  func broadcastValueWillChange<Value>(newValue: Int) {
    // action
  }
}
```

ただこのコードには様々な問題があります。
まず `_age.register(self)` を手動で呼び出す必要があるため、忘れる可能性があります。

また、`broadcastValueWillChange<Value>(newValue:)` 内で `age` プロパティを読むとメモリの排他アクセス違反が起きる可能性があるため、アクセスしてはいけません。

試しに以下のようなコードを実行してみます。

```swift
class Person: Observed {
  @BroadcastObservable var age: Int = 0
  
  init() {
    _age.register(self)
  }
  
  func broadcastValueWillChange<Value>(newValue: Int) {
    print(age)
  }
}

var person = Person()

for i in Array(1...5) {
  person.age = i
}
```

`person.age` が変更されるたびに print するようにしています。

これを実行すると以下のようなエラーを吐いてクラッシュしてしまいます。

```
Simultaneous accesses to 0x100cba3a0, but modification requires exclusive access.
Previous access (a modification) started at `Person.age.setter + 85 (0x100002db5).
Current access (a read) started at:
...
```

`Person.age` の setter 内で `Person.age` を読むのはダメですよと言われていますね。 

これらの問題を解決するために、以下のような構文が提案されています。

```swift
@propertyWrapper
struct BroadcastObservable<Value> {
  private var stored: Value
  
  init(wrappedValue: Value) {
    self.stored = wrappedValue
  }
  
  static subscript<OuterSelf: Observed>(
      instanceSelf observed: OuterSelf,
      wrapped wrappedKeyPath: ReferenceWritableKeyPath<OuterSelf, Value>,
      storage storageKeyPath: ReferenceWritableKeyPath<OuterSelf, Self>
    ) -> Value {
    get {
      observed[keyPath: storageKeyPath].stored
    }
    set {
      let oldValue = observed[keyPath: storageKeyPath].stored
      if newValue != oldValue {
        observed.broadcastValueWillChange(newValue: newValue)
      }
      observed[keyPath: storageKeyPath].stored = newValue
    }
  }
}
```

`var wrappedValue: Value` が消えて代わりに `static subscript<OuterSelf: Observed>(instanceSelf:wrapped:storage:)` が生えました。

そして `BroadcastObservable` へのアクセスは `observed` (ラップしたプロパティを持っているインスタンス) と `storageKeyPath` (e.g. `_age` プロパティの KeyPath) を使ってアクセスします。

これで少し複雑になりましたが、`wrappedValue` の setter アクセス時に getter が呼ばれることがなくなったため、メモリの排他アクセス違反が起きなくなりました。

上のような構文を書くと使う側では以下のように展開されます。

```swift
class Person: Observed {
  @BroadcastObservable var age: Int = 0
  
  // ↑ は ↓ に展開される
  private var _age: BroadcastObservable<Int> = BroadcastObservable(wrappedValue: 0)
  public var age: Int {
    get { BroadcastObservable<Int>[instanceSelf: self, wrapped: \Person.age, storage: \Person._age] }
    set { BroadcastObservable<Int>[instanceSelf: self, wrapped: \Person.age, storage: \Person._age] = newValue }
  }
}
```

これで安全にラップされたプロパティを持つインスタンスへのアクセスができるようになりますね！

また、副次的な効果として、`static subscript<OuterSelf: Observed>(instanceSelf:wrapped:storage:)` は展開されると `self` を引数にとるため、 static プロパティに対して適用できません。
また、現状 `ReferenceWritableKeyPath` を引数にとるため、 class 以外に適用できません(たぶん)。

なので、もし Property Wrapper 型を static プロパティや class 以外に適用されて欲しくない場合は、以下のように書けます。

```swift
@availability(*, unavailable) 
var wrappedValue: Value {
  get { fatalError("only works on instance properties of classes") }
  set { fatalError("only works on instance properties of classes") }
}
```

たまにラップされたプロパティを持つインスタンスにアクセスしたくなるときがあるので、ぜひ実装されて欲しいです。

### 他のプロパティに移譲する
現状、 `@SomeWrapper` を宣言すると暗黙的に `_` がついたバッキングフィールドが追加されます。
ただ、もう既にあるプロパティをバッキングフィールドに使いたい場合があるかもしれません。

そのときに以下のように指定できるようにしようぜという提案。

```swift
lazy var fooBacking: SomeWrapper<Int>
@wrapper(to: fooBacking) var foo: Int
```

上の例では直接プロパティを入れていますが、 KeyPath のように `\.someProperty.someOtherProperty` のようにも指定できるようにするかなども考えられています。

---
以上、軽くですが Property Wrapper について解説しました。
Property Wrapper は他のプログラミング言語ではあまり見かけない、しかしテンプレート的な記述を減らせる可能性を秘めていると思います。

もしプロジェクトでバッキングフィールドを駆使しているプロパティがあったなら、それは Property Wrapper に置き直せるかもしれません。