티스토리 뷰

공부

extension Reactive

Zedd0202 2021. 4. 28. 23:27
반응형

 

안녕하세요 :) Zedd입니다.

저는 막 Reactive를 extension하여 써본적이 없는데...굉장히 일반적이고 많이 사용되는 방법 같더라구요~

공부해보려고 합니다!

 

# Reactive

RxSwift를 보면, Reactive.swift파일이 있습니다.

constrained protocol extensions을 위한 custom point로 'Reactive'를 사용하라고 하는데요. 

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

일반적인 패턴은 다음과 같습니다.

1. Reactive를 extension하고 Base에 대한 constrain(제한)을 건다

extension Reactive where Base: SomeType { } 

이런식

2. extension안에 SomeType에 대한 특정 reactive extension을 구현한다.

 

# Example1 - ControlEvent

아주 간단한 예를 들어보자.  button의 탭을 감지하고 싶다. 

self.myButton.rx
    .controlEvent(.touchUpInside)
    .bind {
       print("button tapped!")
    }
    .disposed(by: self.disposeBag)

이렇게 직접 controlEvent를 호출하여 tap을 감지할 수도 있지만,

UIButton쪽에서는

self.myButton.rx
    .tap
    .bind {
       print("button tapped!")
    }
    .disposed(by: self.disposeBag)

tap이라는 프로퍼티가 이미 있다.

이는

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

이렇게 Reactive를 extension하여 tap이라는 프로퍼티를 구현해놓았기 때문.

ControlEvent - 값을 관찰 할 수는 있지만, 값을 주입시키지는 못한다. 

 

# Example2 - ControlProperty

ControlProperty를 리턴시킬수도 있다. 

ControlProperty - 값을 주입시킬수도 있고, 값의 변화도 관찰 할 수 있는 타입

예를들어 UITextField의 text는 ControlProperty이다. 

extension Reactive where Base: UITextField {
    /// Reactive wrapper for `text` property.
    public var text: ControlProperty<String?> {
        value
    }
}

값을 주입시킬수도 있고, 값의 변화도 관찰할 수 있게 만들고 싶다면 위와같이 Reactive를 extension하면 된다.

1. 값 주입이 가능하기 때문에

textField.rx.text.onNext("안녕")

2. 값을 관찰 할 수 있기 때문에 

textField.rx.text.subscribe(onNext: { _ in })

이런것이 가능.

 

# Example 3 - Binder

Binder를 리턴시킬수도 있다.

Binder - 값을 주입시킬수는 있지만, 값을 관찰하는 것은 불가능

예를들어 UILabel의 text는 Binder이다.

Binder. 즉 관찰하는 것이 불가능 하므로 subscribe 호출 불가. 

 

이미 UIButton+Rx에 있지만;;; UIButton에 setImage를 하고 싶다고 하자.

setImage는 관찰할 수 있게 하기 보다는 값을 주입시키는 역할이다. 

그러므로 Binder로 정의하는 것이 맞음. 

 

myButton.rx~ 이런 플로우로 setImage할 방법이 없을 때 

extension Reactive를 사용하면 된다. 

extension Reactive where Base: UIButton {
    
    public func zeddCustomImage() -> Binder<UIImage?> {
        
    }
}

이렇게 Binder를 리턴하도록 만들어준다. (image값을 주입시켜줄거니까 UIImage타입을 넣어줌) 

Binder를 리턴해야하니까 Binder를 만들어주면 되는데, 

Binder의 원형(?)은 이렇게 생겼다.

Target이 있고, binding쪽에는 Target과 넘겨줄 value 파라미터를 넣어주면 된다.

extension Reactive where Base: UIButton {
    
    public func zeddCustomImage() -> Binder<UIImage?> {
        return Binder(self.base) { (button, image) in
            button.setImage(image, for: [])
        }
    }
}

이런식.

self.myButton.rx
    .zeddCustomImage()
    .onNext(UIImage(systemName: "heart"))

그래서 이렇게 해주면

이런식으로 setImage를 할 수 있다. 

물론 이 메소드는 UIButton+Rx쪽에 image()로 잘 구현이 되어있다;

 

 

# 참고. ControlEvent와 ControlProperty 공통점, 차이점

공통점

- RxCocoa Traits종류 

- MainScheduler에서 실행 (애초에 Traits이 UI처리를 위한 Observable이니 당연)

- 절대 실패 X, 에러 X (이것도 당연)

- control이 dealloc되면 Complete 이벤트 방출

 

차이점

[ControlProperty]

- share(replay: 1) behavior

 

[ControlEvent]

- 구독시 초기값 보내지 않음. 

 

 

혹시 틀린 부분이 있다면 댓글 부탁드립니다! 

 

참고

github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#controlproperty--controlevent

 

반응형

'공부' 카테고리의 다른 글

앱 종료하기 with animation  (2) 2021.05.04
FlexLayout 사용해보기  (4) 2021.05.02
PinLayout 사용해보기  (0) 2021.04.21
Carthage 사용해보기  (0) 2021.04.17
Carthage  (0) 2021.04.15