SwiftUIでState value
を扱っているとき、 $value
とすることで value
のBindingを取得することができる。この $value
がどういう実装になっているのかさっぱり不明だったので、調べた。
結論から言うと、Property Wrappersの projectedValue
が機能している。Property Wrappersを作るとき、基本的には wrappedValue
を使う。ここで projectedValue
も定義しておくと、 $value
としたときに特別な別の値を返却することができるという仕組みだ。
/// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0258-property-wrappers.md#copy-on-write protocol Copyable: AnyObject { func copy() -> Self } @propertyWrapper struct CopyOnWrite<Value: Copyable> { init(wrappedValue: Value) { self.wrappedValue = wrappedValue } private(set) var wrappedValue: Value var projectedValue: Value { mutating get { if !isKnownUniquelyReferenced(&wrappedValue) { wrappedValue = wrappedValue.copy() } return wrappedValue } set { wrappedValue = newValue } } }
このコードは、CopyOnWriteの実装をしている。何もしなければmutableな値になっているが、 $value
とすることで変更可能な値を取り出せる。1
mutable/immutableの管理だったり、DBアクセスできる/できないの区別だったりがSwift Evolutionの中で触れられているが、うまく使えば面白いイディオムが作れる言語仕様だなと思った。普段はやらないが、明示的に実施させたい特別なアクションを表現するのに便利だと思う。
swift-evolution/proposals/0258-property-wrappers.md at main · swiftlang/swift-evolution · GitHub
おまけ
ちなみに、 $value
という識別子はSwiftでは元々許可されていなく、LLDBのデバッグセッションで使われていた。ただ、このプロポーザルによって $value
が使えるよう変更されたようだ。ただし、新しく値を作ることはできず、参照することができるだけ。
swift-evolution/proposals/0258-property-wrappers.md at main · swiftlang/swift-evolution · GitHub