Swift 프로퍼티 래퍼

2 분 소요

프로퍼티 래퍼 이해하기

프로퍼티 래퍼(property wrapper):

  • 연산 프로퍼티의 기능(변환 작업, 유효성 검사)을 개별 클래스와 구조체와 분리
  • 앱 코드에서 재사용

간단한 예제

<연산 프로퍼티 이용>

struct Address {
     
    private var cityname: String = ""
     
    var city: String {
        get { cityname }
        set { cityname = newValue.uppercased() }
    }
}

var address = Address()

address.city = "London"
print(address.city) // LONDON

도시 이름의 문자열을 대문자로 변환

동일한 작업이 다른 구조체나 클래스에 필요할 때,
<프로퍼티 래퍼 정의>

@propertyWrapper
struct FixCase {
    private(set) var value: String = ""
     
    var wrappedValue: String {
        get { value }
        set { value = newValue.uppercased() }
    }
     
    init(wrappedValue initialValue: String) {
        self.wrappedValue = initialValue
    }
}

@propertyWrapper 지시자를 이용하여 선언
클래스나 구조체 안에 구현
게터와 세터 코드가 포함된 wrappedValue 프로퍼티 포함
초기화 메서드는 선택 사항

<프로퍼티 변수에 적용하여 재사용>

struct Contact {
    @FixCase var name: String
    @FixCase var city: String
    @FixCase var country: String
}
 
var contact = Contact(name: "John Smith", city: "London", country: "United Kingdom")
print("\(contact.name), \(contact.city), \(contact.country)")
//JOHN SMITH, LONDON, UNITED KINGDOM

동작이 필요한 클래스나 구조체의 선언부에 있는 프로퍼티 선언 앞에 @FixCase 지시자를 붙임


여러 변수와 타입 지원하기

<여러 값을 받는 프로퍼티 래퍼>

@propertyWrapper
struct MinMaxVal {
    var value: Int
    let max: Int
    let min: Int
     
    init(wrappedValue: Int, min: Int, max: Int) {
        value = wrappedValue
        self.min = min
        self.max = max
    }
     
    var wrappedValue: Int {
        get { return value }
        set {
            if newValue > max {
                value = max
            } else if newValue < min {
                value = min
            } else {
                value = newValue
            }
        }
    }
}

init 메서드는 래퍼 값에 추가된 min과 max 값을 받아 구현

struct Demo {
    @MinMaxVal(min: 100, max: 200) var value: Int = 100
}

var demo = Demo()
demo.value = 150
print(demo.value) // 150

demo.value = 250
print(demo.value) // 200 최댓값

<동일한 타입의 다른 값과 비교할 수 있는 프로퍼티 래퍼>

  • 특정 프로토콜을 따르는 모든 타입과 작업하도록 구현 가능
  • 목적은 비교작업 -> Comparable 프로토콜을 따르는 모든 데이터 타입을 지원하도록 수정
@propertyWrapper
struct MinMaxVal<V: Comparable> {
    var value: V
    let max: V
    let min: V
     
    init(wrappedValue: V, min: V, max: V) {
        value = wrappedValue
        self.min = min
        self.max = max
    }
     
    var wrappedValue: V {
        get { return value }
        set {
            if newValue > max {
                value = max
            } else if newValue < min {
                value = min
            } else {
                value = newValue
            }
        }
    }
}

Comparable 프로토콜을 따르는 다른 모든 타입에도 사용가능

struct Demo {
    @MinMaxVal(min: "Apple", max: "Orange") var value: String = ""
}

var demo = Demo()
demo.value = "Banana"
print(demo.value) //Banana

demo.value = "Pear"
print(demo.value) //Orange 최댓값

문자열 값이 알파벳 관점에서 최솟값과 최댓값 범위 안에 들어오는지 판단

struct DateDemo {
    @MinMaxVal(min: Date(), max: Calendar.current.date(byAdding: .month, value: 1, to: Date())! ) var value: Date = Date()
}

var dateDemo = DateDemo()

print(dateDemo.value) // 디폴트 현재 날짜

dateDemo.value = Calendar.current.date(byAdding: .day, value: 10, to: Date())!
print(dateDemo.value) // 10일 뒤 날짜

dateDemo.value = Calendar.current.date(byAdding: .month, value: 2, to: Date())!
print(dateDemo.value) // 최댓값

Comparable 프로토콜을 따르는 Date 객체를 이용하여 동작