SwiftUI 를 공부하면서, 프로퍼티래퍼를 사용하고 $(달러사인)를 사용하여 값을 전달해주기도 하였다.
이때 사용되는 개념인 wrappedValue와 projectedValue에 대해 자세히 공부해보고 싶어졌다.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
/// Creates the state with an initial wrapped value.
///
/// Don't call this initializer directly. Instead, declare a property
/// with the ``State`` attribute, and provide an initial value:
///
/// @State private var isPlaying: Bool = false
///
/// - Parameter wrappedValue: An initial wrappedValue for a state.
public init(wrappedValue value: Value)
/// Creates the state with an initial value.
///
/// - Parameter value: An initial value of the state.
public init(initialValue value: Value)
/// The underlying value referenced by the state variable.
///
/// This property provides primary access to the value's data. However, you
/// don't access `wrappedValue` directly. Instead, you refer to the property
/// variable created with the ``State`` attribute. In the following example,
/// the button's label depends on the value of `isPlaying` and its action
/// toggles the value of `isPlaying`. Both of these accesses implicitly
/// rely on the state property's wrapped value.
///
/// struct PlayButton: View {
/// @State private var isPlaying: Bool = false
///
/// var body: some View {
/// Button(isPlaying ? "Pause" : "Play") {
/// isPlaying.toggle()
/// }
/// }
/// }
///
public var wrappedValue: Value { get nonmutating set }
/// A binding to the state value.
///
/// Use the projected value to pass a binding value down a view hierarchy.
/// To get the `projectedValue`, prefix the property variable with a dollar
/// sign (`$`). In the following example, `PlayerView` projects a binding
/// of the state property `isPlaying` to the `PlayButton` view using
/// `$isPlaying`:
///
/// struct PlayerView: View {
/// var episode: Episode
/// @State private var isPlaying: Bool = false
///
/// var body: some View {
/// VStack {
/// Text(episode.title)
/// .foregroundStyle(isPlaying ? .primary : .secondary)
/// PlayButton(isPlaying: $isPlaying)
/// }
/// }
/// }
///
public var projectedValue: Binding<Value> { get }
}
@State의 정의를 보면 위와 같다.
주석으로 상당히 설명이 자세히 되어있다.
정리해보면 다음과 같다.
이를 통해 초기값은 wrappedValue로 정의하고 이후, projectedValue를 통해 Binding 타입이 반환이 됨을 알 수 있다.
실제로 Swift 5.1 에서 추가된 Property Wrapper의 개념이 잘 적용이 된 것을 알 수 있다.