본문 바로가기
코딩 공부/Swift

SwiftUI Property Wrappers

by JH-M 2023. 3. 9.

Property Wrapper?

SwiftUI에서 Property Wrappers는 특별한 기능을 갖는 속성을 정의하는 데 사용됩니다. Property Wrappers를 사용하면 코드를 간결하고 읽기 쉽게 만들 수 있습니다.

 

Property Wrappers는 속성에 적용되는 래퍼(Wrapper)로, 속성의 값에 추가적인 로직을 적용하거나, 값이 변경될 때 다른 동작을 수행할 수 있도록 해줍니다. 이는 Swift의 속성 관찰자를 대체할 수도 있습니다.

 

SwiftUI에서 가장 많이 사용되는 Property Wrappers는 @State, @Binding, @ObservedObject, @EnvironmentObject, @FetchRequest입니다.

 

  • @State: SwiftUI 뷰의 상태를 저장하는 데 사용되며, 값을 변경할 때마다 뷰가 다시 렌더링됩니다. 보통 UI 컴포넌트의 상태를 저장하는 데 사용됩니다.
  • @Binding: 부모 뷰에서 자식 뷰로 값을 전달하고, 자식 뷰에서 부모 뷰로 값을 다시 전달하는 데 사용됩니다. 부모 뷰의 속성을 자식 뷰에서 변경하는 데 유용합니다.
  • @ObservedObject: 외부에서 제공되는 ObservableObject 프로토콜을 준수하는 객체의 상태를 저장하는 데 사용됩니다. 뷰에서 @ObservedObject 속성을 참조하면 해당 객체가 변경될 때마다 뷰가 다시 렌더링됩니다.
  • @EnvironmentObject: 앱의 전역 상태를 저장하는 데 사용됩니다. 앱의 다른 뷰에서도 동일한 객체에 접근하여 상태를 공유할 수 있습니다.
  • @FetchRequest: CoreData에서 데이터를 검색하는 데 사용됩니다. 뷰에서 CoreData의 적절한 엔터티와 조건을 설정하여 데이터를 가져올 수 있습니다.

 

Property Wrappers를 사용하면 코드를 간결하게 유지할 수 있으며, 재사용 가능한 코드를 만들기 위한 추상화 수준을 높일 수 있습니다. 또한, SwiftUI에서는 Property Wrappers를 사용하여 다양한 데이터 흐름 패턴을 쉽게 구현할 수 있습니다.

 

 

Property Wrapper 샘플코드

다음은 SwiftUI에서 @State와 @Binding Property Wrappers를 사용한 간단한 예제 코드입니다.

 

struct ContentView: View {
    @State private var isOn = false
    
    var body: some View {
        VStack {
            Toggle(isOn: $isOn) {
                Text("Toggle")
            }
            Text("Toggle is \(isOn ? "on" : "off")")
        }
    }
}

 

위의 코드에서는 @State Property Wrapper를 사용하여 isOn 변수를 선언하고, 이 변수를 Toggle 뷰와 Text 뷰에서 참조하고 있습니다. Toggle 뷰에서 $isOn을 사용하여 isOn 변수를 바인딩하고 있습니다. 이것은 @Binding Property Wrapper를 사용하는 것입니다.

 

Toggle 뷰에서 isOn 변수가 변경될 때마다 뷰가 다시 렌더링됩니다. 또한, isOn 변수가 변경될 때마다 Text 뷰에서 텍스트가 업데이트됩니다.

 

위의 예제에서는 @State와 @Binding Property Wrappers를 사용하여 SwiftUI에서 상태를 저장하고 속성을 바인딩하는 방법을 보여줍니다. 이러한 Property Wrappers를 사용하면 SwiftUI에서 뷰와 상태를 쉽게 관리할 수 있습니다.

 

커스텀 Property Wrapper 만들기

SwiftUI에서 Property Wrapper를 만드는 방법은 다음과 같습니다.

 

1. Property Wrapper 타입 선언

먼저 Property Wrapper를 선언합니다. Property Wrapper는 struct 혹은 class로 만들 수 있습니다. 예를 들어, @CustomWrapper라는 이름의 Property Wrapper를 만들면 다음과 같이 선언할 수 있습니다.

 

struct CustomWrapper<Value> {
    var wrappedValue: Value

    init(initialValue: Value) {
        self.wrappedValue = initialValue
    }
}

 

2. Property Wrapper 프로토콜 구현

Property Wrapper를 선언하면, 이제 프로토콜을 구현해야 합니다. Property Wrapper는 프로토콜을 준수해야 하며, 이를 통해 특정 동작을 수행할 수 있습니다. 가장 일반적인 프로토콜은 Value 혹은 Object를 관찰하는 Observable 프로토콜입니다.

 

@propertyWrapper
struct CustomWrapper<Value> {
    private var value: Value

    init(wrappedValue: Value) {
        self.value = wrappedValue
    }

    var wrappedValue: Value {
        get { value }
        set { value = newValue }
    }

    var projectedValue: CustomWrapper<Value> {
        get { return self }
        set { self = newValue }
    }
}

 

위의 코드에서 @propertyWrapper 키워드를 사용하여 CustomWrapper가 Property Wrapper임을 나타냅니다. 그리고 wrappedValue 변수를 사용하여 Property Wrapper에 래핑된 값을 가져오거나 설정할 수 있습니다.

 

3. Property Wrapper 사용

Property Wrapper를 구현한 후, 이제 Property Wrapper를 사용할 수 있습니다. 예를 들어, CustomWrapper를 사용하여 name 속성을 래핑하고 있는 Person 구조체가 있다고 가정해 보겠습니다.

 

struct Person {
    @CustomWrapper(wrappedValue: "JH") var name: String
}

 

위의 코드에서는 @CustomWrapper Property Wrapper를 사용하여 name 속성을 래핑하고 있습니다. 이제 Person 구조체의 인스턴스를 만들면, 기본값으로 "JH"가 할당됩니다.

 

var person = Person()
print(person.name) // 출력: JH

person.name = "DH"
print(person.name) // 출력: DH

 

위의 코드에서는 Person 구조체의 인스턴스를 만들고, name 속성의 값을 변경하여 출력합니다. Property Wrapper를 사용하면 특별한 동작을 수행하도록 속성을 구성할 수 있습니다.

 

위에서 @CustomWrapper Property Wrapper로 선언한 프로퍼티에 다음과 같이 3개의 변수로 접근 가능합니다.

  • self._name -> CustomWrapper<Value>구조체를 가리킵니다.
  • self.name -> self._name.wrappedValue와 같습니다. Value 타입의 변수를 가리킵니다. 위 예제에서는 String 타입으로 치환됩니다.
  • self.$name -> self._name.projectedValue와 같습니다. CustomWrapper<Value> 구조체를 가리킵니다. 

 

참고

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties

 

Documentation

 

docs.swift.org

 

댓글