apple/RxSwift, ReactorKit

Extension Reactive (RxSwift)

lgvv 2022. 1. 12. 17:24

Extension Reactive (RxSwift)

RxSwift는 커스터마이징을 확장하여 사용할 수 있음.

  • RxSwift 내부 코드를 분석하다보면 Reactive.swift 파일에 custom포인트로 사용하라고 안내하고 있음

 

연관포스팅


목차

  • Reactive.swift 파일 살펴보기
  • RxSwift 내부에서 사용하는 Reactive.swift 어떻게 사용하는지 보기
  • 몇가지 예제
    • UIViewController + Rx
    • UIButton + Rx
    • UITextField + Rx
  • Binder 내부 구현 살펴보기

 

Reactive.swift 파일 살펴보기

 Use `Reactive` proxy as customization point for constrained protocol extensions.

 General pattern would be:

 // 1. Extend Reactive protocol with constrain on Base
 // Read as: Reactive Extension where Base is a SomeType
 extension Reactive where Base: SomeType {
 // 2. Put any specific reactive extension for SomeType here
 }

 With this approach we can have more specialized methods and properties using
 `Base` and not just specialized on common base type.

 */

public struct Reactive<Base> {
    /// Base object to extend.
    public let base: Base

    /// Creates extensions with base object.
    ///
    /// - parameter base: Base object.
    public init(_ base: Base) {
        self.base = base
    }
}



 

RxSwift 내부에서 사용하는 Reactive.swift 어떻게 사용하는지 보기

아래처럼 확장해서 사용하고 있음

extension Reactive where Base: UIButton {
    
    /// Reactive wrapper for `TouchUpInside` control event.
    public var tap: ControlEvent<Void> {
        return controlEvent(.touchUpInside)
    }
}

 

 

들어가기 전 기본 용어 간단 정리

  • ControlEvent - 값을 관찰할 수는 있으나, 값을 주입시키지는 못함.
  • ControlProperty - 값을 주입시킬 수도 있고, 값의 변화도 관찰할 수 있다.
  • Binder - 값을 주입시킬 수 있으나, 값의 변화는 관찰하지 못함.

 

UIViewController+Rx

RxSwift를 Extension으로 바꾸기

import UIKit
import RxCocoa
import RxSwift

extension Reactive where Base: UIViewController {
    var viewWillAppear: ControlEvent<Void> {
        let source = self.methodInvoked(#selector(Base.viewWillAppear(_:))).map { _ in }
        return ControlEvent(events: source)
    }
}

 

Usage

	rx.viewWillAppear // UIViewController+Rx에 정의
            .asObservable() // 반환되는 이벤트를 옵저버블 타입으로 받는다. (공식문서 link에 의하면 From으로 연결되어 있음)
            .bind(to: viewWillAppearSubject)
            .disposed(by: disposeBag)

 


UIButton+Rx

import UIKit
import RxCocoa
import RxSwift

extension Reactive where Base: UIButton {
    // 같은 기능을 재사용하기 위한 목적!
    public var customChangeTitle: Binder<String> {
        let s = Binder<String>(base) { btn, text in
            btn.setTitle(text, for: [])
        }
        return s
    } // 같은 기능을 재사용하기 위한 목적!
}

위 아래 두 코드는 target이 base임으로 같은 기능을 한다. 

extension Reactive where Base: UIButton {
    // 같은 기능을 재사용하기 위한 목적!
    public var customChangeTitle: Binder<String> {
        let s = Binder<String>(base) { _, text in
            base.setTitle(text, for: [])
        }
        return s
    } 
}
Binder<String>(base, binding: {}) // 클로저 구문 이렇게도 작성 가능

 

Usage

함수가 사라져서 훨씬 더 깔끔해짐

 

         myButton.rx
            .tap
            .map{ "text" }
            .bind(to: myButton.rx.customChangeTitle)
            .disposed(by: bag)

 

UITextField+Rx

ControlProperty는 값을 주입할 수 있기 때문에 아래처럼 사용할 수 있음

import UIKit
import RxCocoa
import RxSwift

extension Reactive where Base: UITextField {
   
    public var text: ControlProperty<String?> {
        value
    }
}
textField.rx.text.onNext("Hello") // 값을 주입할 수 있기 때문에
textField.rx.text.subcribe(onNext: { _ in }) // 값을 관찰할 수 있기 때문에

 

 

Binder 원형에 대해서 살펴보기

extension으로 Binder를 사용하려고 하는 경우에는 target을 받아야 함.

Binder

 

 

 

'apple > RxSwift, ReactorKit' 카테고리의 다른 글

iOS RxAction (RxSwift Community)  (0) 2022.01.19
RxSwift ch 18. Table & Collection views  (0) 2022.01.18
iOS RxDelegateProxy 만들어보기 #1  (0) 2022.01.12
iOS RxStarScream 총정리  (0) 2022.01.12
RxSwift 06 RxDataSources  (0) 2021.08.19