[RxSwift]Binder에 대한 고찰

유경박·2023년 8월 31일
0

RxSwift를 하면서 보통 데이터 바인딩을 할 때 아래와같이 text를 바인딩시키곤 한다.

let labelTextSubject = BehaviorSubject<String>(value:"Initial Text")
labelTextSubject
.bind(to: button.rx.title(for: .normal))
.disposed(by: disposeBag)

결론부터 말하면 이 코드는 Binder를 사용하여 UI에 업데이트 시키는 구조인데
Binder라는 애가 어떤앤지 먼저 살펴보자


주목해야 할것은 녹색상자로 마킹된 scheduler부분과 클로저로 받을 Target과 Value이다.

사진을 보면, Binder의 경우 기본적으로 메인스레드에 사용될 RxSwift 메인스케쥴러를 사용하기 때문에 UI Update와 관련돼서 스케쥴러를 따로 지정해줄필요가없다.따라서 파랑색 박스 안에 있는 클로저로 사용될 인자로 Target과 값타입을 지정해주면 UI 업데이트 관련 바인딩이 가능하다.

예를들면, ViewController에 있는 버튼을 클릭했을때 bool변수로 따로 관리 하고 지정을 하고 싶다면

..
.bind(IsPressed)
..
..    
extension ViewController{
var IsPressed: Binder<Bool> {
    return Binder(self) { [weak self] vc, isPressed in
        guard let self = self else{return}
        label.text = String(isPressed)}
    }
}

와 같은 형태가 될것이다.(쉽게 바인딩할 Target, 바인딩될변수의 타입 정도로 생각하자)

이제 이 Binder의 내용을 예제와 함께 더 자세히 알아보자.

예제는 처음 실행시 버튼 title에 "Initial Text"를 바인딩하고, 버튼을 클릭할때마다 true/false를 반복해서 출력하는 간단한 예제이다.(뷰모델을 생략)

1)프로퍼티 변수로 다음과 같이 설정한다.

@IBOutlet weak var button: UIButton!
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag()
let labelTextSubject = BehaviorSubject<String>(value: "Initial Text")
let isPressedSubject = BehaviorSubject<Bool>(value : false)

2)labelTextSubject값을 버튼 타이틀에 데이터 바인딩을 한다

labelTextSubject
  .bind(to: button.rx.title(for: .normal))
  .disposed(by: disposeBag)
  • labetextSubject의 초기값Observable(String)값을 button title에 바인딩 시킨다.

3)버튼을 탭할때 true/false를 바인딩

button.rx.tap
 .flatMap{ [weak self] _ -> Observable<Bool> in
            guard let self = self else { return .empty() }
            return isPressedSubject.take(1).map { !$0 }
        }
 .bind(to: isPressedSubject)
 .disposed(by: disposeBag)
  • 버튼을 탭할때 ispressedSubject가 갖고 있는 값(Observable(bool))을 반전시킨 후 반환하여 isPressedSubject 변수에 바인딩 시킨다.

4)IsPressed라는 Binder 변수 바인딩

isPressedSubject
  .bind(to: IsPressed)
  .disposed(by: disposeBag) 
  • 버튼을 탭할때 최종적으로 true/false값으로 데이터 바인딩된 변수(isPressedSubject)를 IsPressed변수에 다시한번 바인딩 시킨다.

5)IsPressed 변수를 통해 들어오는 값을 통해 라벨 Text 변경

extension ViewController{
var IsPressed: Binder<Bool> {
    return Binder(self) { [weak self] vc, isPressed in
        guard let self = self else{return}
        label.text = String(isPressed)
     }
  }
}
  • IsPressed 변수는 클로저로 반환되는 ViewController와 isPressed 값을 갖고 label에 UI에 업데이트 시킨다.

Binder의 흐름이 이해가 되는가?

Subjects를 통해 결국 평소에 잘 모르고 사용했던
label.rx.text/button.rx.title같은것은 모두 Binder의 형태이며, Observable형태의 값을 갖고있을 때 바로 UI에 업데이트시켜 값을 바인딩하는 구조였다.

마지막으로, 굳이 Binder 변수를 안만들어도 기능은 똑같으니 변수로 관리하기 싫어!라고 생각이 들 수 있다.

하지만, 위의 예제를 통해서 그리고 여러 오픈 소스 라이브러리들을 참고하면서 Binder변수를 따로 관리하여 느낀점 및 장점은 다음과 같다

1)데이터 바인딩을 할때 변수가 많아지거나 로직이 복잡해질경우 변수로 따로 관리하여 가독성이 좋아지고 디버깅 및 유지관리에 쉬워진다.

2)MainScheduler에서 실행되기때문에 동시성 관련 업데이트를 할때 안정적인 UI 업데이트 처리가 가능하다.

3)변수로 관리하기 떄문에 모듈화 및 재사용성에도 증가된다.

-다음 챕터는 Control Event..

profile
으아아

0개의 댓글