Tunko Development Diary

RxSwift) RxCocoa 란? 본문

Development/RxSwift

RxSwift) RxCocoa 란?

Tunko 2022. 6. 12. 06:35

RxCocoa는 cocoa touch 프레임워크에 reactive 의 장점을 더해준 라이브러리입니다.

RxCocoa는 RxSwift를 기반으로 구동됩니다.

cocoapods install을 통해서 RxSwift와 함께 설치해주어야 합니다.

podfile

pod 'RxSwift', '6.2.0'
pod 'RxCocoa'

설치된 프로젝트에서 Pods/Pods/RxCocoa/UIButton+Rx.swift 파일을 열어보면

extension Reactive where Base: UIButton {
    public var tap: ControlEvent<Void> {
        controlEvent(.touchUpInside)
    }
}

이런식으로 확장되어 있습니다.

UIButton 을 직접적으로 확장하는것이 아니라 Base타입을 지정해서 확장해줍니다.

지네릭 (Generics)

요약하면 Reactive은 반드시 UIButton 프로토콜을 따라야 한다고 제한합니다.

클래스를 Rx 방식으로 확장할때 Reactive를 사용합니다.

Reactive에 선언된 @dynamicMemberLookup 속성덕분에 원하는 클래스내에 속성들이 자동으로 바인딩됩니다.

Pods/Pods/RxSwift/Reactive.swift

Reactive 원형

@dynamicMemberLookup
public struct Reactive<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    } 
    public subscript<Property>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Property>) -> Binder<Property> where Base: AnyObject {
        Binder(self.base) { base, value in
            base[keyPath: keyPath] = value
        }
    }
}

제네릭으로 전달 받은 형식을 base에 초기화 해줍니다. 그리고 base타입이 가지고있는 Property를 Binder형태로 합성해줍니다.

public protocol ReactiveCompatible { 
    associatedtype ReactiveBase 
    static var rx: Reactive<ReactiveBase>.Type { get set }
    var rx: Reactive<ReactiveBase> { get set }
}

extension ReactiveCompatible {
    public static var rx: Reactive<Self>.Type {
        get { Reactive<Self>.self }
        set { }
    }
    public var rx: Reactive<Self> {
        get { Reactive(self) }
        set { }
    }
}

ReactiveCompatible 프로토콜은 Reactive로 확장된 타입에 rx라는 속성을 추가해 줍니다. rx라는 네임스페이스를 추가합니다.

대부분의 Reactive에 접근할때 rx 속성을 통해 접근하게 됩니니다.

그리고 마지막에 이렇게 NSObject가 확장되어있습니다.

import Foundation
extension NSObject: ReactiveCompatible { }

NSObject는 cocoa touch에서 모든 클래스가 상속받는 root 클래스 입니다. 즉, 모든 cocoa touch 클래스에 rx라는 속성이 추가 됩니다.

이제 실제로 storyboard 에 UIViewController 와 연결된 클래스에서 UILabel과 UIButton 에 대한 동작을 만들어보겠습니다.

class TestViewController : UIViewController {
    @IBOutlet weak var testLabel: UILabel!
    @IBAction func tap(_ sender: Any) {
        testLabel.text = "터치"
    }
}

버튼을 터치하면 UILabel의 글자가 바뀌는 코드입니다.

이것을 Rx를 사용한 코드로 바꿔보겠습니다.

class TestViewController : UIViewController {
    @IBOutlet weak var testLabel: UILabel!
		@IBOutlet weak var testButton: UIButton!
		let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad() 
        testButton.rx.tap
            .map{ "터치" }
            .subscribe { [weak self] text in
                self?.testLabel.text = text
            }
            .disposed(by: disposeBag)
    }
}

이렇게 바뀌었습니다. 생각보다 4줄짜리 코드가 더 길어졌습니다.

testButton.rx.tap 이란 옵저버블에

map 연산자를 통해서 “터치” 를 넣고

subscribe 를 통해 구독해줍니다.

UIButton+Rx.swift 파일에서 보았듯 UIButton에 tap이 확장 구현 되어있었습니다.

따라서 UIButton.rx 내부에 tap 이라는 터치 이벤트에 응답하는 옵저버블이 있습니다.

단도직입적으로 말하자면 옵저버블은 아닙니다.

ControlEvent 라는 형식으로 확장되어있지만 UI전용 옵저버블이라고 생각하시면 됩니다.

 

1. 터치이벤트를 바로 받아서

2. map을 통해 데이터를 전달

3. 구독과 동시에 원하는 UILabel에 적용해줍니다.

 

따로 스토리보드를 통해서 연결해줄 필요도 없고 UIButton과 UILabel만 스토리보드에 연결되어있다면 바로 터치 이벤트에 대한 반응을 추가 할 수 있습니다.

 

번외로 .subscribe 코드를 더 줄일 수 있습니다.

testButton.rx.tap
            .map{ "터치" }
            .bind(to: testLabel.rx.text)
            .disposed(by: disposeBag)

이렇게 말이죠.

self?.testLabel.text 는 cocoa touch가 제공하는 옵셔널 속성입니다.

testLabel.rx.text 는 Rxcocoa 가 제공하는 바인더 속성입니다.

rx.text 는 바인더 속성입니다. UILabel.text의 속성과는 다르다는걸 꼭 알아둡시다.

이정도만해도 각 UI에서 사용했던 딜리게이트 이벤트 처리와 터치 이벤트등을 보다 쉽게 간결하게 구현할수 있게 됩니다.

반응형

'Development > RxSwift' 카테고리의 다른 글

RxSwift) RxCocoa Traits(Driver)  (0) 2022.06.13
RxSwift) RxCocoa Binding  (0) 2022.06.12
RxSwift) Error Handling (retry, retry(when:))  (0) 2022.06.10
RxSwift) Error Handling (catch, catchAndReturn)  (0) 2022.06.10
RxSwift) Scheduler  (0) 2022.06.09
Comments