Swift 5.1에 소개된 Property Wrapper는 제게 굉장히 생소한 개념이었습니다. 번역도 어떻게 해야 할지 모르겠네요. "속성 싸개" 라고 해야 하나? 아무튼 속성을 감싸는 녀석이라는 뜻인데, 도대체 속성을 감싼다는 것이 무슨 의미인지조차 감을 잡기 힘들었습니다.

하지만 아마 이렇게 시작하면 많은 분들이 감을 잡기 쉬울 것 같습니다. 아래와 같은 구조의 코드는 그래도 익숙하지 않나요?

var isFlagged: BehaviorRelay<Bool> = BehaviorRelay<Bool>(value: false)

위 코드를 기준으로 말하면, false 라는 값을 BehaviorRelay<Bool>(value:... 라는 녀석이 감싸고 있다고 볼 수 있습니다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23a87b89-512a-4067-9628-7f2a260ee8e6/Screen_Shot_2019-09-03_at_9.05.23_PM.png

이 때 falsewrappedValue 가 되고, BehaviorRelay가 false 라는 property를 wrap 하는 propertyWrapper 가 됩니다.

물론 위의 코드에서 BehaviorRelay는 propertyWrapper를 사용해 만든 클래스가 아닙니다. PropertyWrapper는 이런 BehaviorRelay와 같은 코드를 훨씬 더 깔끔하게 만들 수 있게 해 주는 녀석입니다.

결론적으로 PropertyWrapper를 사용해 BehaviorRelay를 다시 작성한다면, 아래와 같이 쓸 수 있게 됩니다.

@BehaviorRelay isFlagged = false

이 비교를 하기 위해, 먼저 propertyWrapper를 사용하지 않고 BehaviorRelay를 구현해 보겠습니다. BehaviorRelay는 RxCocoa를 사용하시는 분들은 굉장히 익숙하게 사용하시는 클래스인데, 기본적으로 "isFlagged"와 같은 값이 변화 했을 때 다른 객체들이 이를 구독하기 쉽게 만들어 줍니다. 원래는 다른 Rx연산자들을 활용해 더 섬세하게 구현되어 있지만, 지금은 개념학습이 목적이므로 NotificationCenter를 이용하여 간단하게 흉내만 내 볼게요.

class BehaviorRelay<Element> {
    var value:Element
    let notiCenter = NotificationCenter()
    init(value: Element) {
        self.value = value
        postValueChange(newVal: value)
    }
    
    func accept(_ newValue: Element) {        
        self.value = newValue
        postValueChange(newVal: newValue)
    }
    
    func bind( onNext: @escaping (Element) -> Void ) {
        notiCenter
            .addObserver(forName: NSNotification.Name("valueChange"),
                         object: nil,
                         queue: nil,
                         using: { noti in
                            let newElement = noti.userInfo?["newValue"] as! Element
                            onNext(newElement)
            })
    }
    
    private func postValueChange(newVal: Element) {
        notiCenter
            .post(name: NSNotification.Name("valueChange"),
                  object: self,
                  userInfo: ["newValue":newVal])
    }
}

이렇게 만들어진 BehaviorRelay는 아래와 같이 사용 될 수 있습니다.

var isFlagged:BehaviorRelay<Bool> = BehaviorRelay<Bool>(value: false)

isFlagged
    .bind(onNext: { result in
        print(result) // true를 비동기로 출력
     })

isFlagged.accept(true)