Old_SWIFT(221012)/기본이야기

Property Wrapper에 대하여

KataRN 2022. 10. 5. 00:27
반응형

안녕하세요. KataRN입니다.

 

오늘은 Property Wrapper에 대해 알아보겠습니다.

 

솔직히 너무 어렵네요 ㅠㅠ

최대한 풀어서 써보겠습니다...

 

우선 Property Wrapper의 정의는?

- 어떤 값이 있으면 이 값을 한 번 감싸서 저장을 위한 로직과 얻어오기 위한 로직을 어느정도 분리해서 반복을 줄여줄 수 있는 방법을 제공하는 속성입니다.

 

하.. 어렵다 어려워...

 

다른분들의 글과 똑같지만 다르게...(창의성이 부족하여... 참고하여...)... 예를 들어보겠습니다.

 

이마트에서 아이스크림을 팝니다.

행사를 해서 천원 이상은 무조건 천원이라고 하네요.

그럼 500원은? 500원이겠죠?

1500은? 1000원!

 

이걸 코드로 표현하면 아래와 같습니다.

struct Emart {
//	1. 최대 금액을 설정
	private var maxPrice: Int = 1000
    
//	2. 접근제어자를 Private으로 설정하여 직접 접근 방지
	private var _icecreamPrice: Int
    
//	3. setter를 이용하여 값 저장시 maxPrice를 넘을 수 없게 함.
	var icecreamPrice: Int {
		get { return _icecreamPrice }
		set { _icecreamPrice = min(newValue, maxPrice)}
	}
}

 

그런데 이벤트가 확장되어 과자, 물, 쌀, 껌도 하겠답니다...

이걸 적용하면 아래와 같습니다.

 

struct Emart {
	private var maxPrice: Int = 1000
	private var _icecreamPrice: Int
	private var _snackPrice: Int
	private var _waterPrice: Int
	private var _ricePrice: Int
	private var _gumPrice: Int
    
    var icecreamPirce: Int {
    	get { return _icecreamPrice }
        set { _icecreamPrice = min(newValue, maxPrice }
    }
    
    var snackPrice: Int {
    	get { return _snackPrice }
        set { _snackPrice = min(newValue, maxPrice }
    }
    
    var waterPrice: Int {
    	get { return _waterPrice }
        set { _waterPrice = min(newValue, maxPrice }
    }
    
    var ricePrice: Int {
    	get { return _ricePrice }
        set { _ricePrice = min(newValue, maxPrice }
    }
    
    var gumPrice: Int {
    	get { return _gumPrice }
        set { _gumPrice = min(newValue, maxPrice }
    }
}

 

이거 뭔가 비효율적이네하고 생각해볼만한건 역시 enum으로 묶어서 처리하기?

struct Emart {
    enum Product {
    	case icecream
        case snack
        case water
        case rice
        case gum
    }
    
    private var maxPrice: Int = 1000
    private var icecreamPirce: Int
    private var snackPirce: Int
    private var waterPirce: Int
    private var ricePirce: Int
    private var gumPirce: Int
    
    func getPrice(_ prudct: Product) -> Int {
    	switch product {
        case .icecream: return icecreamPirce
        case .snack: return snackPirce
        case .water: return waterPirce
        case .rice: return ricePirce
        case .gum: return gumPirce
        }
    }
    
    mutating func setPrice(food: Food, price: Int) {
        let realPrice = min(price, maxPrice)
        switch food {
        case .icecream: self.icecreamPrice = realPrice
        case .snack: self.snackPrice = realPrice
        case .water: self.waterPrice = realPrice
        case .rice: self.ricePrice = realPrice
        case .gum: self.gumPrice = realPrice
   }
}

 

이제 Property Wrapper을 사용해봅시다.

@propertyWrapper
struct MaxPriceOrLessWrapper {
    private var max = 1000
    private var value = 0
    
    var wrappedValue: Int {
        get { return value }
        set { value = min(newValue,max) }
    }
}

struct Emart {
    @MaxPriceOrLessWrapper var icecreamPrice: Int
    @MaxPriceOrLessWrapper var snackPrice: Int
    @MaxPriceOrLessWrapper var waterPrice: Int
    @MaxPriceOrLessWrapper var ricePrice: Int
    @MaxPriceOrLessWrapper var gumPrice: Int
}

📢 사용법

struct를 만들고 그 위에(혹은 앞에) @propertyWrapper를 붙여준다.

그러면 wrappedValue라는것을 선언해줘야하는데, 이 Property Wrapper가 붙은 모든 값은 wrappedValue 연산프로퍼티로 값을 뱉고 정의한다. (안써주면 오류납니다.)

 

상당히 간결해졌네요.

여기에 초기값을 무조건 0으로 해야되는가?

 

@propertyWrapper
struct MaxPriceOrLessWrapper {
    private var max: Int
    private var value: Int
    
    init(value: Int, maxPrice: Int) {
        self.max = maxPrice
        self.value = min(value, maxPrice)
    }
    
    var wrappedValue: Int {
        get { return value }
        set { value = min(newValue,max) }
    }
}

struct Emart {
    @MaxPriceOrLessWrapper(value: 9000, maxPrice: 10000) 
    var icecreamPrice: Int
    
    @MaxPriceOrLessWrapper(value: 12000, maxPrice: 10000) 
    var snackPrice: Int
    
    @MaxPriceOrLessWrapper(value: 7500, maxPrice: 10000) 
    var waterPrice: Int
    
    @MaxPriceOrLessWrapper(value: 400, maxPrice: 500) 
    var ricePrice: Int
    
    @MaxPriceOrLessWrapper(value: 1000, maxPrice: 500) 
    var gumPrice: Int
}

 

이처럼 중복도 없고~ 너무 좋네요~

UserDefaults 예제가 꼭 나오는데 확인한번 해보도록하죠.

class UserManager { 
	static var usesTouchID: Bool {
		get { return UserDefaults.standard.bool(forKey: "usesTouchID") }
		set { UserDefaults.standard.set(newValue, forKey: "usesTouchID") }
	}
      
	static var myEmail: String? {      
        	get { return UserDefaults.standard.string(forKey: "myEmail") }
		set { UserDefaults.standard.set(newValue, forKey: "myEmail") }
	}

	static var isLoggedIn: Bool {      
		get { return UserDefaults.standard.bool(forKey: "isLoggedIn") }
		set { UserDefaults.standard.set(newValue, forKey: "isLoggedIn") }
	}
}

 

이것을 Property Wrapper를 이용해서 바꿔보도록하죠.

@propertyWrapper
struct UserDefault<T> {
	let key: String
	let defaultValue: T

	var wrappedValue: T {
		get { UserDefaults.standard.object(forKey: self.key) as? T ?? self.defaultValue }
		set { UserDefaults.standard.set(newValue, forKey: self.key) }
	}
}

class UserManager {
	@UserDefault(key: "usesTouchID", defaultValue: false)
	static var usesTouchID: Bool

	@UserDefault(key: "myEmail", defaultValue: nil)
	static var myEmail: String?

	@UserDefault(key: "isLoggedIn", defaultValue: false)
	static var isLoggedIn: Bool
}

 

Property Wrapper가 반복될 수 있는 구문을 간편하고 알아보기 쉽게 만들기에 좋네요.

그리고 UserDefaults에 가장 잘 활용할 수 있다고하니까 활용해보는것도 좋을것 같습니다.

저는 솔직히 오늘 이후로 UserDefault는 propertyWrapper을 사용할 것 같습니다.

 

다만 좀 어려웠던 내용이었던지라 제 생각은 별로 담지 못한점 죄송합니다.

더 노력하겠습니다.

 

오늘도 긴글 읽어주셔서 감사합니다.

 

- 참고 : https://dongminyoon.tistory.com/52, https://zeddios.tistory.com/1221, https://jiseobkim.github.io/swift/2021/06/13/swift-Property-Wrapper.html

반응형