環境
Swift 5.1
Xcode 11.0 (11A420a)
2019年9月現在SwiftUIは変化が大きく、当記事に示した挙動もバージョンを経ると変更される可能性が十分にあるため、ご注意ください。
執筆動機
Swift5.1にてProperty Wrappersという機能が追加されました。
ですが当方、foo、_foo、$foo、wrappedValue、projectedValueがそれぞれ何を意味しているのか...と、
それぞれの関係性が掴みあぐねておりまして...😇
ですが、先日のこちら(YouTubeに遷移します)の発表とその資料で非常に丁寧に解説されており、自分でも手を動かすことで、理解を深めることができました。
複雑に考えすぎていたのですが、その実態はすごくシンプルだったんですね...。😅
すなわち。ほぼ引用してしまう形ですが...。
@Hoge var foo: Int
と書くと、
var foo: Int
var _foo: Hoge<Int>
に展開されるような形となる。
foo は、_foo.wrappedValue と等価。
$foo は、_foo.projectedValue と等価。
ルールとしてはたったこれだけでした。
例えばHogeクラスが、このような定義で、いまelement にIntの42 が入っていて...
// @propertyWrapperは、クラス・構造体・列挙体に付加可能
@propertyWrapper
struct Hoge<T> {
var element: T // 今、ここが :Int = 42 だとする
// これはgetterのみ定義しているが、望むならsetterを定義することも可能
var wrappedValue: T { element }
// これはgetterのみ定義しているが、望むならsetterを定義することも可能
var projectedValue: Hoge<T> { Hoge(element: element) }
}
このようなプロパティがあるとしたら...
// ここの型アノテーションは、移譲先の型の wrappedValueの型と一致しなければならない
// 一致しないときは、Property type '誤って指定した型名' does not match that of the 'wrappedValue' property of its wrapper type 'wrappedValueの型' というコンパイルエラーが出力される
@Hoge var foo: Int
foo と書くと、wrappedValue のgetter呼び出し
42
$fooと書くと、projectedValue のgetter呼び出し
Hoge<Int>(element: 42)
になります。
wrappedValue にアクセスするには、
foo_foo.wrappedValue
projectedValue にアクセスするには、
$foo_foo.projectedValue
それぞれ2通りの方法がある、ということですね。
後者は冗長な書き方ということになると思うので、基本的に前者の書き方でアクセスしていけばいいかと思います。
SwiftUI Property WrappersクラスのwrappedValue、projectedValue一覧表
ということで、SwiftUIで頻出するProperty WrappersクラスのwrappedValueとprojectedValueが返す値の型をまとめ、表に起こしてみました。
| propertyWrapperクラス | wrappedValue |
projectedValue |
|---|---|---|
State<Value> |
Value { get nonmutating set } |
Binding<Value> { get } |
Binding<Value> |
Value { get nonmutating set } |
Binding<Value> { get } |
ObservedObject<ObjectType> |
ObjectType { get } |
ObservedObject<ObjectType>.Wrapper { get } |
EnvironmentObject<ObjectType> |
ObjectType { get } |
EnvironmentObject<ObjectType>.Wrapper { get } |
Environment<Value> |
Value { get } |
× (未定義のためコンパイルエラー) |
参考
Why I Can Mutate @State var? How Does @State Property Wrapper Work Inside?