環境
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?