ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Property Wrapper
    Swift 2022. 3. 25. 23:55

    안녕하세요.

     

    요즘 SwiftUI에 대해서 학습을 하고 있는 와중에 @State니 @Binding이니 낯선 키워드들을 접하게 되었습니다.

     

    그러고 보니 Combine을 사용하면서 @Published와 같은 Property Wrapper들을 사용하고 있었는데요.

     

    오늘은 그 Property Wrapper에 대해서 알아보도록 하겠습니다.


    1. Property Wrapper

    https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617

     

    Properties — The Swift Programming Language (Swift 5.6)

    Properties Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. Computed properties a

    docs.swift.org

    Property Wrapper를 한국말로 생각해보면 속성 포장지(?)라는 뜻을 가지고 있는데요.

     

    저는 특정 프로퍼티에 특징을 부여한다!라는 뜻으로 이해했습니다.

     

    @State의 경우에는 읽고 쓸 수 있는 프로퍼티라고 선언하는 것이고

    @Published 같은 경우는 값을 방출할 수 있는 프로퍼티라고 말하는 것이죠.

     

    만약  여러 프로퍼티들에서 같은 getter/setter가 반복해서 사용되는 경우

    해당 코드를 Property Wrapper를 통해 정의해주게 되면 재사용성이 올라가고 프로퍼티를 정의하는 부분의 가독성이 좋아집니다.

     

    그럼 Property Wrapper를 직접 정의하는 방법은 매우 간답합니다.

    바로 @propertyWrapper 어노테이션을 구조체, 클래스, 열거형 앞에 붙여주면 됩니다.

    그렇게 타입을 생성하면 wrappedValue, 즉 포장된 값을 포함하지 않다고 합니다.

     

    TwelveOrLess 타입은 저장하려는 값과 12 중에 더 작은 값을 가지는 타입입니다.

    따라서 다음과 같이 정의할 수 있겠죠.

    @propertyWrapper
    struct TwelveOrLess {
        private var number: Int = 0
    
        var wrappedValue: Int {
            get { return number }
            set { number = min(newValue, 12) }
        }
    }

    이렇게 정의한 Property Wrapper는 다른 타입의 프로퍼티 앞에 @TwelveOrLess를 붙여서 사용할 수 있습니다.

     

    struct Rectangle {
        @TwelveOrLess var height: Int
        @TwelveOrLess var width: Int
    }

    하지만 이렇게만 정의하면 문제가 조금 있습니다.

    바로 생성자의 변수의 Int 타입이 아니라 Property Wrapper타입인 것입니다.

     

    이러한 문제는 Property Wrapper에 init(wrappedValue: Int)를 추가하므로 해결할 수 있습니다.

     

    @propertyWrapper
    struct TwelveOrLess {
        private var number: Int = 0
    
        var wrappedValue: Int {
            get { return number }
            set { number = min(newValue, 12) }
        }
    
        init(wrappedValue: Int) {
            self.wrappedValue = wrappedValue
        }
    }

     

    위와 같이 wrappedValue를 매개변수로 하는 생성자는 프로퍼티에 값이 할당된 경우 호출됩니다.

    따라서 위와 같이 구조체에서 height와 width에 초기값을 주었을 때도 init(wrappedValue:)가 호출되는 것이죠.

     

    따라서 초기값을 부여하지 않고 생성할 때도 의도한 대로 Int형을 이용해 인스턴스를 생성할 수 있습니다.

     

    또한 Property Wrapper는 추가적인 기능을 제공할 수 있는 projectedValue라는 프로퍼티를 추가할 수 있습니다.

     

    공식문서에서는 값의 변화 여부를 판단하는 데 사용하였습니다.

    @propertyWrapper
    struct TwelveOrLess {
        private var number: Int = 0
        private(set) var projectedValue: Bool = false
    
        var wrappedValue: Int {
            get { return number }
            set {
                if newValue > 12 {
                    number = 12
                    projectedValue = true
                } else {
                    number = newValue
                    projectedValue = false
                }
            }
        }
    
        init(wrappedValue: Int) {
            self.wrappedValue = wrappedValue
        }
    }

    위와 같이 Bool 타입의 projectedValue를 선언하고 12보다 값이 커서 값이 변경된 경우에는 projectedValue를 true로 설정했습니다.

     

    projectedValue는 외부에서 해당 프로퍼티의 앞에 $를 붙여서 사용할 수 있습니다.

     

    꼭 Bool 타입이 아니라 다른 타입으로도 선언이 가능하니 상황에 맞게 사용하면 될 것 같습니다.

     

     

    'Swift' 카테고리의 다른 글

    Model-View-Presenter(MVP)  (0) 2022.04.04
    Model-View-Controller(MVC)  (0) 2022.04.03
    SOLID 원칙  (0) 2022.01.28
    Codable  (0) 2021.12.28
    NSCoder  (0) 2021.12.27
Designed by Tistory.