What is '@propertyWrapper' in Swift

ios dev·2022년 3월 19일
0

Swift

목록 보기
3/3
post-thumbnail

@propertyWrapper

propertyWrapper는 프로퍼티가 저장되는 방식을 관리하는 코드와 프로퍼티를 정의하는 코드 사이에 분리 계층을 추가한다.

예를 들어, 기본 데이터를 UserDefaults에 저장해야 하는 여러 가지 프로퍼티가 있는 경우 모든 프로퍼티에 UserDefaults를 관리하는 코드를 작성해야 하는데

propertyWrapper를 사용할 경우 UserDefaults 관리 코드를 한 번 작성한 다음 여러 프로퍼티에 재사용할 수 있다.


Apply

Declaration Attributes 중 하나인 propertyWrapper를 클래스, 구조체 또는 열거형 Declaration에 적용하면 해당 타입을 propertyWrapper로 사용할 수 있다.

@propertyWrapper
struct SomeWrapper {
}

propertyWrapper를 타입에 적용하면 해당 타입과 이름이 같은 사용자 정의 attribute가 생성된다.

@SomeWrapper

타입의 프로퍼티를 래핑하기 위해서는 생성된 attribute를 프로퍼티에 적용해야 한다.

struct SomeStruct {
	@SomeWrapper var someValue: Int
}

⚠️ Computed variables, global variables, and constants can’t use property wrappers.


WrappedValue

wrapper에는 반드시 wrappedValue 인스턴스 프로퍼티를 정의해야 한다. 타입의 프로퍼티에 래핑되는 값은 wrappedValue의 getter & setter로 노출되는 값이다.
(대부분의 경우 wrappedValue는 계산된 값이지만 저장된 값으로도 사용할 수 있다.)

@propertyWrapper
struct SomeWrapper {
	private var someValue = 0
    
	var wrappedValue: Int {
    	get { 
        	return someValue
        }
        set { 
        	someValue = newValue 
        }
    }
}

Synthesized Storage

propertyWrapper는 래핑된 값에 필요한 기본 저장소를 정의하고 관리할 수 있다. 컴파일러는 래핑된 타입의 이름에 underscore(_)를 접두사로 붙여 wrapper 타입의 인스턴스에 대한 저장소를 합성한다.
propertyWrapper에 대한 합성 저장소에는 private 수준의 접근 제어를 적용해야 한다.

struct SomeStruct {
	private var _someValue = SomeWrapper()
    
	var someValue: Int {
    	get {
        	return _someValue
        }
        set { 
	        _someValue = newValue
        }
    }
}

@propertyWrapper 초기화

Swift는 propertyWrapper의 초기화를 위해 두 가지 형태의 구문을 제공한다.

@propertyWrapper
struct SomeWrapper {
	var wrappedValue: Int
    
    init() {
    	self.wrappedValue = 10
    }
    
    init(wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
}

예를 들어, SomeStruct는 SomeWrapper가 정의하는 초기화를 호출한다.

struct SomeStruct {
	// Uses init()
    @SomeWrapper var a: Int /// -> 10
    
    // Uses init(wrappedValue:)
    @SomeWrapper var b = 100 /// -> 100
    @SomeWrapper(wrappedValue: 999) var c: Int ///  -> 999
}

Projected Value

wrapped property에 대한 projected value는 wrapped property가 추가 기능을 표시하는 데 사용할 수 있는 두 번째 값이다. wrapped property의 이름에 dollar sign ($)을 접두사로 붙여 호출하면 projected value에 접근할 수 있다.

projected valueoriginal wrapped property와 동일한 접근 제어 수준을 갖는다.

Example. Use Projected Value

Projected Value를 사용하면 정규성 검사를 통과하지 못한 번호가 무엇인지 확인할 수 있다.

@propertyWrapper
struct PhoneNumber {
    private var phone = ""
    
    var projectedValue: String?
    var wrappedValue: String {
        get { return phone }
        set {
            projectedValue = validate(newValue) ? nil : newValue
            phone = validate(newValue) ? newValue : "Invalid PhoneNumber"
        }
    }
    
    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue
    }
    
    private func validate(_ phoneNumber: String) -> Bool {
        let validFormat = "^01[0-1, 7][0-9]{7,8}$"
        let phoneNumberPredicate = NSPredicate(format: "SELF MATCHES %@", validFormat)
        return phoneNumberPredicate.evaluate(with: phoneNumber)
    }
}

struct User {
    @PhoneNumber var phone: String
}

let user = User(phone: "01012345678")
user.phone /// -> "01012345678"
user.$phone /// -> nil

let user = User(phone: "12345678")
user.phone /// -> "Invalid PhoneNumber"
user.$phone /// -> "12345678"





cf.
https://docs.swift.org/swift-book/ReferenceManual/Attributes.html

0개의 댓글