안녕하세요 오늘은 SwiftUI에서 사용되는 몇가지 Property Wrapper에 대해 알아보겠습니다.
그전에 먼저 Property Wrapper를 공식 문서를 통해 정리해 보겠습니다.
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
코드처럼 Property를 저장하는 방법을 관리하는 코드와 정의하는 코드 사이에 분리 계층을 추가합니다.
Wrapper를 사용하기 위해서는 Property 앞에 Wrapper를 추가합니다.
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
rectangle.height = 10
print(rectangle.height) // 10
rectangle.height = 24
print(rectangle.height) // 12보다 크기 때문에 12가 출력된다.
위 코드처럼 사각형의 높이를 24로 설정하게 되면 TweleveOrLess
에서 허용하는 수인 12보다 큰 수이기 때문에 24대신 12를 저장하게 됩니다.
Property Wrapper도 initializer를 통해 값을 초기화 할 수 있습니다.
@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)
}
}
이 중 3번째 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)
narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
두 번째 출력 구문의 결과는 5, 4입니다. Property Wrapper를 초기화하는 과정에서 maximum을 각각 5,4로 초기화했기 때문입니다.
마지막으로 Projecting Value를 이용해 추가 기능을 노출할 수 있습니다.
이름에서처럼 Property Wrapper를 다른 값에 투영하는 것입니다.
@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) // false
someStructure.someNumber = 55
print(someStructure.$someNumber) // true
위 코드는 Projected Value를 이용해 초기화되는 값이 적합한지를 판단하고 있습니다.
이뿐만 아니라 다양한 방법으로 활용될 수 있는데 간단한 예시를 가져와 보겠습니다.
@propertyWrapper
struct USDate {
var wrappedValue: Date
var projectedValue: String {
let formatter = DateFormatter()
formatter.dateFormat = "MM-dd-YYYY"
return formatter.string(from: wrappedValue)
}
}
struct Person {
var name: String
@USDate var birthDate: Date
}
let me = Person(name: "Stewart", birthDate: Date())
print(me.$birthDate) // "02-11-2023"
초기화한 Date 값을 Formatter를 통해 우리가 알아보기 쉬운 형태로 바꿀 수도 있습니다.