SwiftUIの「$」って何ですか？

## $ってなに

よくこのようにTextFieldなどで、stateプロパティに「\$」をつけたものを渡します。
「$」をつけると値の変更を検知してプロパティを更新してくれるようなる
と何となく理解していましたが、理解が不十分なので調べてみました。

```swift
@State private var text1 = ""

var body: some View {
    TextField("text1", text: $text1)
}
```

### 公式ドキュメントを読んでみる

まずは公式ドキュメントに何か書いてないかと思い
Stateのドキュメントを確認してみました。

https://developer.apple.com/documentation/swiftui/state

>To pass a state property to another view in the view hierarchy, use the variable name with the \$ prefix operator. This retrieves a binding of the state property from its projectedValue property.

Viewの階層下にある他のViewにstateプロパティを渡すには、「$」を変数の先頭つけて使う。
**これはそのprojectedValueからstateプロパティのbindingを取得する。**

太文字部分が「$」をつけてできることを示していることはわかりましたが
またわからないことが出てきました。

- projectedValueってなに

https://developer.apple.com/documentation/swiftui/state/projectedvalue

> A binding to the state value.

StateのprojectedValueを一言で表すと
"state valueへのbinding" のようです。
getterプロパティとして用意されています。

```swift
var projectedValue: Binding<Value> { get }
```

- Bindingってなに

https://developer.apple.com/documentation/swiftui/binding

> A property wrapper type that can read and write a value owned by a source of truth.

一言で表すと **"信頼できる一つの情報源に所有された値を読み書きできるプロパティラッパータイプ"** のようです。 
難しいのでもう少し読んでみます。 

> Use a binding to create a two-way connection between a property that stores data, and a view that displays and changes the data

**データを格納するプロパティとViewを表示したり更新したりするデータの間の双方向接続を作るためにbindingを使用する**

BindingがプロパティとUIの双方向の接続のためのものであることがわかりました。

つまり
「\$」をつけるとprojectedValueというgetterプロパティへアクセスすることになる。
Stateで定義したプロパティのprojectedプロパティは
stateプロパティのbindingを取得することになるので
今回の例の場合 $text は Binding\<String> を表しています。


### TextFieldで実際に確認してみた
TextFieldを使って確認していきます。
TextFieldの第二引数にはBinding\<String\>を渡さなければいけないため
stateプロパティをそのまま渡すとエラーになります。
TextFieldはユーザーの入力を受け付けるので
入力された値とプロパティの値が同期されるように
Bindingでラップした値を入れる必要があるのは理解できますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263661/6e97f1ac-75a0-e899-7598-200d1b8fed9d.png)

#### `@ObservedObject`の`@Published`プロパティの場合どうなる？

もう少し深掘りしていきます。
今度は`@ObservedObject`の`@Published`の値をTextFieldに渡す場合に
どこに「\$」を付けるべきなのかを確認していきます。
４つの選択肢のうちどれが正解でしょうか？

```
$model.text  - ①
$model.$text - ②
model.$text  - ③
model.text   - ④
```

私は`＠Published` のprojectedValueが 値をBindingでラップした値を返すと考え
それがBinding<String>になると思ったので③model.$textかなと思いましたが違っていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263661/1f10016a-4452-2fb1-a1b9-b659cf06a472.png)

正解は①$model.textでした。

他の選択肢はエラーが出たので確認していきます。

##### ③model.$text
予想していたこれからみていきます。
*Cannot convert value of type 'Published<String>.Publisher' to expected argument type 'Binding<String>'*
'Binding<String>'ではなく'Published.Publisher'になっているようです。
公式ドキュメント確認すると
`＠Published`のprojectedValueは`Published<Value>.Publisher`でした。
そのため model.$text は Published<String>.Publisher を表しており、不正解です。

https://developer.apple.com/documentation/combine/published

##### ②$model.$text
*Cannot convert value of type 'Binding<Published<String>.Publisher' to expected argument type 'Binding<String>'*
'Binding<Published<String>.Publisher'になっています。
`@ObservedObject`のprojectedValueを確認すると
`ObservedObject<ObjectType>.Wrapper`になっていましたがパッとみてわかりません。
説明文を読むとこうありました。
>A projection of the observed object that creates bindings to its properties using dynamic member lookup.

理解しづらいですが私は
保持しているプロパティのBindingを動的に生成する監視オブジェクトの投影
つまり
複数のプロパティを持つBindingだと飲み込みました。

https://developer.apple.com/documentation/swiftui/observedobject

そのため \$model.$text は Binding\<Published<String>.Publisher> を表しており、不正解です。

##### ④model.text
これは単純にStringを表しているので不正解です。



### `@ObservedObject`をネストさせたら・・・
さらに`@ObservedObject`の中に`@ObservedObject`を入れてみたらこの２通りがエラーになりませんでした。

```swift
TextField("text", text: $model.model.text)
TextField("text", text: model.$model.text)
```
ただビルドして入力してみるとこのようなwarningが出ました。
*Binding<String> action tried to update multiple times per frame.*
おそらく上記のような使い方はしないほうがよさそうです。
これに関しては別記事にしたいと思います。


### 次に記事にしたいこと
- ObservedObjectをネストした場合の「＄」の使い方
- SwiftUIの「@」って何ですか？