프로퍼티 관찰자는 프로퍼티 값이 변경되는지 관찰하고 응답한다. 이는 현재 값이 새로운 값과 같아도 값이 설정되는 시기에 호출된다. 다음의 경우에 관찰자 추가가 가능하다.
- 정의한 저장된 프로퍼티
 - 상속 저장된 프로퍼티
 - 상속 계산된 프로퍼티
 
class StepCounter {
	var totalStep : Int = 0 {
    	willSet(newTotalSteps) {
        	print("\(newTotalSteps)")
        } didSet {
        	if totalSteps > oldValue {
            	print("\(totalSteps - oldValue)")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
//윌셋에서 200 출력, 디드셋에서 200(200-0)출력한다.
stepCounter.totalSteps = 360
//윌셋에서 360, 디드셋에서 160(360-200)을 출력한다.
//바뀌기 직전과 직후를 출력하므로 둘다 출력이 되고, 꼭 둘다 쌍으로 선언할 필요는 없다.
//oldValue를 통해서 디드셋에서 바뀌기 전 값 접근이 가능하다는게 포인트다.
@propertyWrapper
struct TwelveOrLess {
	private var number = 0
    var wrappedValue : Int {
    	get {
        	return number
        }
        set {
        	number = min(newValue, 12)
        }
    }
    
    //게터는 저장된 값을 반환하고, 세터는 새로운 값이 12보다 작거나 같은걸 확인하고 반환한다. 
    //프로퍼티 래퍼는 wrappedValue를 프로퍼티의 값으로 반환해주게 된다.
struct SmallRectangle {
	@TwelveOrLess var height : Int
    @TwelveOrLess var width : Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
//0을 출력한다.
rectangle.height = 10
//새로운 값을 넣어줬으니 setter가 작용할 것이다.
print(rectangle.height)
//아직 12보다 작으므로 10을 출력한다.
rectangle.height = 24
//세터가 작용하고 12보다 크므로 12를 출력한다.
@propertyWrapper
struct SmallNumber {
	private var maximum : Int
    private var number : Int
    
    var wrappedValue : Int {
    	get { return number }
        set { number = min(newValue, maximun) }
//여기에서 보면 현재 구조체 내 프로퍼티에 값이 하나도 정의되지 않은 상태이다.   
	init() {
    	maximum = 12
        number = 0
        
    init(wrappedValue : Int) {
    	maximun = 12
        number = min(wrappedValue, maximun)
    }
    
    init(wrappedValue : Int, maximum : Int) {
    	self.maximum = maximum
   	    number = min(wrappedValue, maximun)
    }
    //기본적으로 3개의 초기화를 지원한다. 
struct ZeroRectangle {
	@SmallNumber var height : Int
    @SmallNumber var width : Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
//0, 0을 출력한다. 가장 첫 init() 이 호출되기 때문이다.
struct UnitRectangle {
	@SmallNumber var height : Int = 1
    @SmallNumber var width : Int = 1
}
var unitRectangle = UnitRectngle()
print(unitRectangle.height, unitRectangle.width)
//1, 1을 출력한다. 초기값을 설정해줘도 init()을 호출한다. 
//초기값으로 wrappedValue를 전달하지 않았기에 그렇다.
struct NarrowRectangle {
	@SmallNumber(wrappedValue : 2, maximun : 5) var height : Int
    @SmallNumber(wrappedValue : 3, maximun : 4) var width : Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
//2, 3을 출력한다. 다음에서는 래핑한 값을 통해서 초기화가 가능하다. 
//위의 예시에서 3번에 해당하는 초기화를 사용하게 된다.
//init(wrappedValue : Int, maximun : Int)