-
[RxSwift] ControlProperty와 ObservableTypeiOS 2024. 8. 5. 09:53
이번 포스트에서는 UITextField.rx.text가 반환하는 ControlProperty 구조체와 ControlProperty가 준수하는 ObservableType에 대해 알아보면서 UITextField.rx.text가 subscribe를 사용할 수 있는 이유를 알아보자.
이전 포스트에서 다루었던 것처럼 Reactive<UITextField> 구조체는 text 연산 프로퍼티를 통해 ControlProperty 구조체를 반환한다
RxCocoa에 구현된 UITextField의 Reactive extension
extension Reactive where Base: UITextField { /// Reactive wrapper for `text` property. public var text: ControlProperty<String?> { value } /// Reactive wrapper for `text` property. public var value: ControlProperty<String?> { return base.rx.controlPropertyWithDefaultEvents( getter: { textField in textField.text }, setter: { textField, value in // This check is important because setting text value always clears control state // including marked text selection which is important for proper input // when IME input method is used. if textField.text != value { textField.text = value } } ) } ...... }위 소스코드의 실행순서를 정리하면 아래와 같다.
UITextField.rx.text
- Reactive의 Extension으로 구현된 연산프로퍼티 text를 호출
- 같은 Extension의 멤버인 연산프로퍼티 value를 재호출
- getter와 setter파라미터에 textField 객체의 text 값을 가져오고 설정하는 클로저들을 매개변수로 넘기고 base.rx.controlPropertyWithDefaultEvents 메서드를 호출해 ControlProperty<String?> 반환
UIControl 클래스를 반환하는 Reactive Extension
import RxSwift import UIKit extension Reactive where Base: UIControl { /// Reactive wrapper for target action pattern. /// /// - parameter controlEvents: Filter for observed event types. public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> { let source: Observable<Void> = Observable.create { [weak control = self.base] observer in MainScheduler.ensureRunningOnMainThread() guard let control = control else { observer.on(.completed) return Disposables.create() } let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in observer.on(.next(())) } return Disposables.create(with: controlTarget.dispose) } .take(until: deallocated) return ControlEvent(events: source) } /// Creates a `ControlProperty` that is triggered by target/action pattern value updates. /// /// - parameter controlEvents: Events that trigger value update sequence elements. /// - parameter getter: Property value getter. /// - parameter setter: Property value setter. public func controlProperty<T>( editingEvents: UIControl.Event, getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> Void ) -> ControlProperty<T> { let source: Observable<T> = Observable.create { [weak weakControl = base] observer in guard let control = weakControl else { observer.on(.completed) return Disposables.create() } observer.on(.next(getter(control))) let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in if let control = weakControl { observer.on(.next(getter(control))) } } return Disposables.create(with: controlTarget.dispose) } .take(until: deallocated) let bindingObserver = Binder(base, binding: setter) return ControlProperty<T>(values: source, valueSink: bindingObserver) } /// This is a separate method to better communicate to public consumers that /// an `editingEvent` needs to fire for control property to be updated. internal func controlPropertyWithDefaultEvents<T>( editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged], getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> Void ) -> ControlProperty<T> { return controlProperty( editingEvents: editingEvents, getter: getter, setter: setter ) } }
이전 포스트
[RxSwift] UILabel이 subscribe를 사용할 수 없는 이유
이 포스트를 읽으면 좋은 분들1. RxSwift의 작동방식을 알고 싶지만 어렵게 느껴지는 분들2. 기존에 subscribe메서드를 사용해왔지만 내부 구조를 살펴볼 여유가 없었던 분들 비동기 프로그래밍
tunastorm.tistory.com
'iOS' 카테고리의 다른 글
[SwiftUI] View Identity (feat. WWDC 2021) (0) 2024.10.31 [RxSwift] Binder와 ObserverType (0) 2024.08.10 [RxSwift] UILabel이 subscribe를 사용할 수 없는 이유 (0) 2024.07.31 Alamofire를 구조화된 코드로 사용하기 (feat. URLRequsetConvertible) (2) 2024.06.19 [UIKit] 화면전환 코드를 프로토콜로 사용하기 (0) 2024.06.04