[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/Combine & Rx

[RxSwift] ControlProperty, ControlEvent

inu 2022. 2. 24. 15:18
반응형

이번엔 RxCocoa의 ControlProperty와 ControlEvent에 대해 알아보아요.


ControlProperty

https://ios-development.tistory.com/181

ControlProperty는 Subject처럼 프로퍼티에 값을 주입할 수 있고 동시에 값의 변화도 관찰할 수 있는 타입입니다. 이는 (UIElement).rx를 통해 접근할 수 있습니다. ControlProperty를 사용하면 해당하는 프로퍼티의 변경사항을 데이터 시퀀스로 받아올 수 있습니다.

 

아래 UITextField+Rx.Swift의 구현 예시를 봅시다.

extension Reactive where Base: UITextField {
    ...
    public var text: ControlProperty<String?> {
        return value
    }
    ...
}

내부에 존재하는 text가 ControlProperty 타입입니다. 이는 (UITextField).rx.text로 접근할 수 있습니다. ControlProperty는 bind(to:) 메서드를 통해 subscribe를 수행할 수 있습니다. (bind(to:)는 일종의 syntax suger로, 사용자에게 좀 더 직관적인 이해를 제공합니다. 내부적인 처리는 subscribe와 동일합니다.)

 

여기서 잠깐! ControlProperty와 함께 많이 활용되는 Binder라는 개념도 존재합니다.

Binder

RxCocoa에는 내부 property를 Observer처럼 제공해 변경사항을 이에 바로 적용할 수 있도록 하는 기능도 존재합니다. 이를 Binder라고 합니다.

 

UILabel+Rx.Swift의 구현 내용을 봅시다.

extension Reactive where Base: UILabel {
    ...
    public var text: Binder<String?> {
        return Binder(self.base) { label, text in
            label.text = text
        }
    }

내부에서 Binder라는 타입을 사용하고 있습니다. 데이터를 받아오면 이를 바로 UI Elements에 적용합니다.

public struct Binder<Value>: ObserverType {
    ...
}

Binder타입의 정의를 보니 이 역시 ObserverType의 일종임을 확인할 수 있네요. 

 

 

어느정도 이해가 되셨나요? ControlProperty와 Binder를 활용해 데이터 시퀀스의 반환 및 적용하는 예시를 보겠습니다.

let results = query.rx.text
    .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
    }

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

query.rx.text라는 ControlProperty를 통해 fetchAutoCompleteItems에 query를 날리고 이 반환값을 resultCount.rx.text라는 binder에 적용하고 있습니다.


ControlEvent

Cocoa에서는 수많은 Event가 발생합니다. 그 이벤트는 ViewController가 Load되면 발생하는 ViewDidLoad에 대한 메세지가 될 수도 있고, User로 인해 발생하는 Tap Event가 될 수도 있습니다. ControlEvent은 이러한 Event들에 대한 시퀀스를 받아올 수 있는 기능입니다.

 

아래 UICollectionView+Rx의 구현 예시를 봅시다.

extension Reactive where Base: UICollectionView {

    /// Reactive wrapper for `delegate` message `collectionView:didSelectItemAtIndexPath:`.
    public var itemSelected: ControlEvent<IndexPath> {
        let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:)))
            .map { a in
                return a[1] as! IndexPath
            }

        return ControlEvent(events: source)
    }
}

내부에서 itemSelected가 ControlEvent라는 타입을 사용하고 있습니다. 이는 (UICollectionView).rx.itemSelected로 접근할 수 있습니다. 이 역시 ControlProperty와 같이 bind하여 필요한 작업을 수행할 수 있습니다.


RxCocoa Traits?

지난 게시글에서 RxCocoa의 Trait에 대해 알아보았는데요. 사실 ControlProperty와 ControlEvent도 Trait의 일종이라고 합니다. RxSwift의 문서에서도 Trait으로 소개하고 있어요!

 

https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#rxcocoa-traits

 

그런데 이렇게되면 조금 헷갈리는게... Control Property는 Subject와 유사한 역할을 하고 있는데, 그럼 Subject도 Trait의 일종으로 볼 수 있는 것일까요? Subject 역시 Observable을 wrapping하고 있으니까요...

 

이런 개념의 분리가 중요한 것은 아니겠지만 확실하게 어떻게 생각하면 좋을지 알고 싶네요. 혹시 아시는 분이 있다면 댓글 부탁드립니다!


참고

 

[RxSwift Book] Chapter 12: Beginning RxCocoa

이 포스트는 RxSwift - Reactive Programming with Swift 책의 챕터 12에서 눈에 띄는 내용들만 요약해 둔 것입니다. 참고 바랍니다.

jusung.github.io

 

 

반응형

'🍎 Apple > Combine & Rx' 카테고리의 다른 글

[RxSwift] Relay (PublishRelay, BehaviorRelay, ReplayRelay)  (0) 2022.02.25
[RxSwift] Driver, Signal  (0) 2022.02.24
[RxSwift] Trait (Single, Completable, Maybe)  (2) 2022.02.24
[RxSwift] Scheduler  (2) 2022.02.24
[RxSwift] Subject  (0) 2022.02.16