본문 바로가기
iOS/Swift배우기

[Swift] PropertyWrapper 사용법

by 붕어사랑 티스토리 2024. 3. 22.
반응형

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

 

Documentation

 

docs.swift.org

1. PropertyWrapper란?

파이썬의 데코레이션과 비슷하다. 파이썬의 데코레이션은 코드 앞뒤로 어떠한 코드를 붙여주는 작업을 해준다.

 

프로퍼티 래퍼또한 비슷하다. 어떤 프로퍼티 앞뒤로 특정한 작업을 넣을 수 있다.

 

 

2. 만드는법

만드는 방법은 간단하다.

 

1. struct를 정의할 때 @PropertyWrapper라고 적어준다

2. struct안에 wrappedValue라는 computedProperty를 정의해주면 된다

 

아래는 범위가 12까지인 프로퍼티를 프로퍼티 래퍼를 이용해 구현하는 예제이다

 

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

 

 

3. 사용법

프로퍼티 래퍼를 사용할 프로퍼티 앞에 @을 이용하여 연결해준다

 

 

아래의 코드에서 height와 width는 프로퍼티 래퍼의 wrappedValue와 연결된다고 생각하면 된다

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


var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"


rectangle.height = 10
print(rectangle.height)
// Prints "10"


rectangle.height = 24
print(rectangle.height)
// Prints "12"

 

 

실제로 위 코드는 컴파일러에 의해서 아래와 같이 번역된다

 

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

 

4. 프로퍼티 래퍼의 초기값 설정

위 예제에서는 바로 0을 넣어서 초기값을 설정해 주었지만 실제로는 아래처럼 init을 이용하여 다양한 방법으로 초기화가 가능하다

 

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int


    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }


    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

 

 

아래처럼 사용하면 첫번째 init()을 사용하게 된다

struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int
}


var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"

 

 

 

아래 내용은 init(wrappedValue:)을 사용하는 예제이다.

기본적으로 PropertyWrapper을 사용한 변수에 assign( = 을이용하여 값 부여) 하면 initializer에 wrappedValue에 맵핑된다!

struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}


var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"

 

 

다음으로는 init(wrappedValue:, maximum:)을 사용하는 예제이다

struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}


var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"


narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"

 

 

앞서 말한것처럼 = 을 이용하면 wrappedValue에 맵핑된다고 했다. 이를 이용하면 아래처럼도 사용 가능하다

struct MixedRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber(maximum: 9) var width: Int = 2
}


var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// Prints "1"


mixedRectangle.height = 20
print(mixedRectangle.height)
// Prints "12"

 

 

 

5. ProjectedValue

프로퍼티 래퍼에는 또하나의 기능이 있다. wrappedValue를 다른값으로 Projecting 할 수 있는 기능이다.

가령 내가 어떤 시간을 int로 표현하면, 이를 년도 월 일 형식으로 바꿔서 표현해줄 수 있는 기능이라고 해야하나

 

아래는 공식문서에서 나온 내용이다. 방법은 간단하다 projectedValue를 정의해주면 된다

 

 

 

아래 코드 설명은 간단하다. 값을 할당하는데 12가 넘으면 true, 아니면 false로 프로젝팅 하고있다.

즉 값이 깍여서 들어왔냐 아니냐를 projectedValue로 표현한 것이다.

 

유용한 기능인지는 잘 모르겠다.

@propertyWrapper
struct SmallNumber {
    private var number: Int
    private(set) var projectedValue: Bool


    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }


    init() {
        self.number = 0
        self.projectedValue = false
    }
}
struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()


someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"


someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"

 

반응형

'iOS > Swift배우기' 카테고리의 다른 글

Xcode에서 SwiftUI 코드선택 익숙해지기  (0) 2024.03.22
[SwiftUI] 자식에게 State 전달하기  (0) 2024.03.21
SwiftUI에서 {} 동작 원리  (0) 2024.03.20
[Swift] Generic이란  (0) 2024.03.20
[Swift] inout 파라미터  (0) 2024.03.20

댓글