[RxSwift] Reactive를 extension 할때 rx 확장자 밑에 확장한 프로퍼티가 붙는 이유

Jin·2021년 12월 24일
0

RxSwift를 이제 막 배우고 있는데 여러가지 의문이 들었습니다.

  1. Reactive를 extension해서 사용할 때 왜 추가한 프로퍼티가 rx 확장자 밑에 붙는건지 이해가 안되었습니다.
  2. 또 모든 UI 관련 class들에 rx확장자가 자동으로 붙어있는것도 왜그런지 궁금했고요.

그래서 소스코드를 조금 살펴보고 결론을 내렸습니다. 아래는 제가 공부한 내용이고 말투는 편하게 하겠습니다.


우선 먼저 이해해야할 것들부터 보자.

Reactive.swift파일은 아래와 같다.

/* Reactive.swift */

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
    }
}

/// A type that has reactive extensions.
public protocol ReactiveCompatible {
    /// Extended type
    associatedtype ReactiveBase

    @available(*, deprecated, message: "Use `ReactiveBase` instead.")
    typealias CompatibleType = ReactiveBase

    /// Reactive extensions.
    static var rx: Reactive<ReactiveBase>.Type { get set }

    /// Reactive extensions.
    var rx: Reactive<ReactiveBase> { get set }
}

extension ReactiveCompatible {
    /// Reactive extensions.
    public static var rx: Reactive<Self>.Type {
        get {
            return Reactive<Self>.self
        }
        set {
            // this enables using Reactive to "mutate" base type
        }
    }

    /// Reactive extensions.
    public var rx: Reactive<Self> {
        get {
            return Reactive(self)
        }
        set {
            // this enables using Reactive to "mutate" base object
        }
    }
}

import class Foundation.NSObject

/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }

우선 ReactiveCompatible를 보도록 하자. extension 부분을 보면 public var rx: Reactive로 선언된 Computed Property를 볼 수 있다. get에는 return Reactive(self)이다.

즉 ReactiveCompatible를 채택할 class에 rx라는 Computed Property를 추가하는데, 그 프로퍼티는 Reactive라는 struct이다. 그리고 이 struct는 앞의 class 타입의 base 프로퍼티를 가지게 된다.

/* 예시 코드 */

extension AClass: ReactiveCompatible { }

let aClass = AClass()

print(type(of: aClass.rx.base)) // AClass가 출력된다.

예를 들어, AClass에 ReactiveCompatible를 채택하면, 이 AClass의 인스턴스(aClass)에는 rx 프로퍼티로 접근할 수 있는 어떤 struct의 인스턴스가 생기고 이 인스턴스는 AClass타입의 base를 갖는다.

(aClass.rx).base라고 생각하면 편하다. 괄호 안 부분이 Reactive라는 struct의 인스턴스이다.

다시 윗쪽의 Reactive.swift파일로 돌아가자. 맨 밑에줄을 보면

extension NSObject: ReactiveCompatible { } 이 있다.

즉 모든 NSObject에 rx속성을 추가하는 것이다. rx속성은 Computed Property이기 때문에 NSObject를 상속받는 모든 class는 자기 자신의 타입을 갖는 rx.base를 가지게 될것이다.

자 이제 UIButton+Rx.swift파일을 보자. 딴건 필요없고 제일 윗쪽을 보면,

/* UIButton+Rx.swift */

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

이렇게 되어있는데 Reactive에 tap속성을 확장시킨 것이다.

설명하면, UIButton의 인스턴스는 rx속성을 가진다(NSObject의 후손이기 때문에). 이때 button.rx는 Reactive 구조체의 인스턴스인데 이 구조체가 tap 속성을 가지므로 확장된 tap은 button.rx.tap처럼 rx로 접근하게 되는 것이다.

정리

  1. textField.rx.text처럼 접근할 때 textField.rx가 Reactive구조체이다. 따라서 Reactive구조체를 extension하면 rx밑에 확장한 속성이 생기는 효과.
  2. UI관련된 클래스들이 NSObject를 상속받고있고, 이 NSObject가 ReactiveCompatible를 채택하고 있기 때문에 자동으로 UI관련된 클래스에 rx속성이 붙는다.
profile
iOS Learner | Rock, Metal Lover

0개의 댓글