ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RxSwift로 리펙토링 맛보기
    iOS/RxSwift 2022. 1. 3. 16:27

    과제중에 다음과 같은 화면을 구현하는것이 있었다.

    ViewController은 View를 소유하고 있어서, ViewController에서 View의 객체들에 addTarget을 구현해줬다.

    override func viewDidLoad() {
        super.viewDidLoad()
        mainView.startButton.addTarget(self, action: #selector(startButtonTapped), for: .touchUpInside)
        mainView.fotterLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(loginLabelTapped)))
    }
    
    @objc func startButtonTapped() {
        changeRootVC(to: SignUpViewController())
    }
    
    @objc private func loginLabelTapped(_ gesture: UITapGestureRecognizer) {
        guard let text = mainView.fotterLabel.text else { return }
        let loginRange = (text as NSString).range(of: "로그인")
    
        if gesture.didTapAttributedTextInLabel(label: mainView.fotterLabel, inRange: loginRange) {
          changeRootVC(to: SignInViewController())
        }
    }

    라벨의 특정부분을 클릭했을 때 gesture을 설정해주는 extension은 didTapAttributedTextInLabel 이곳에서 볼 수 있습니다.

    평소에는 매우 당연하고 자연스러운 코드였지만, 오늘 RxCocoa를 이용한 예제를 연습해보니

    너무 지저분해 보이는 코드로 인식이 변했다..

    구현부와 실행부분을 한번에 써줄 수 있도록 하면 더 깔끔해 보이고, 이해하기도 쉽기 때문이다.

    RxCocoa를 통해서 Button Tap Action을,

    RxGesture을 통해서 Label Tap Action을 구현해보기로 했다.

    결과

    깔끔하게 한줄씩 작성하느라 높이는 길어졌지만, 훨씬 더 가독성 있는 코드가 완성됐다!

    RxCocoa에서는 Infinite Observable Sequence가 발생하므로, 해당 Observable을 subscribe하고있다면

    dispose되기 전까지는 무한정으로 이벤트를 받아볼 수 있다.

    따라서 아래 코드에서는 startButton이 tap될 때 마다 subscribe할 때 작성해준 closure가 실행된다.

    override func viewDidLoad() {
      super.viewDidLoad()
      let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
      navigationItem.backBarButtonItem = backButton
      subscribe()
    }
    
    func subscribe() {
      mainView.startButton.rx
        .tap
        .subscribe { [weak self] _ in
          self?.changeRootVC(to: SignUpViewController())
        }
        .disposed(by: disposeBag)
      mainView.fotterLabel.rx
      // tapGesture에 대해서
        .tapGesture()
      // .recognized된 상태만 filter한 Observable을 리턴해준다.
        .when(.recognized)
      // 해당 Observable에 대해서 이벤트가 발생했을 때, 어떻게 해줄지에 대해서 구현해준다.
        .subscribe { [weak self] gesture in
          guard let self = self,
                let text = self.mainView.fotterLabel.text,
                let gesture = gesture.element else { return }
          let loginRange = (text as NSString).range(of: "로그인")
    
          if gesture.didTapAttributedTextInLabel(label: self.mainView.fotterLabel, inRange: loginRange) {
            self.changeRootVC(to: SignInViewController())
          }
        }
        .disposed(by: disposeBag)
    }

    rx에 대한 간단한 탐구

    위 코드에서 보면 mainView.startButton.rx, mainView.fotterLabel.rx 와 같이 뒤에 rx가 붙는것을 확인할 수 있다.

    이것이 어디서 나오는것인가 한번 확인해보면

    ReactiveCompatible이라는 프로토콜에 선언되어있고

    ReactiveCompatible를 NSObject에서 채택하는 구조이다.

    이 NSObject란 무엇인가 살펴보면

    모든 Obejctive-C의 근원이 되는 클래스이다.

    그렇기 때문에 NSObject에 해당 프로토콜을 채택하게 되면, 모든 UIKit 클래스에서도 rx라는 변수를 사용할 수 있게 된다.

    다시 돌아가서 mainView.startButton.rx의 type은 Reactive<UIButton>이 되는것이다.

    Reactive<UIButton>이 사용할 수 있는 프로퍼티나 메서드는 다음과 같이 정의되어있다.

    Reactive<Base> 에서 BaseUIButton인 것에 대한 extension이라는 뜻이다.

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

    [RxSwift] Error Handling [2]  (0) 2022.01.27
    [RxSwift] Error Handling [1]  (0) 2022.01.25
    [Rxswift] 03.Subjects  (0) 2021.12.19
    [RxSwift] 02.Observables / raw  (0) 2021.12.19
    [RxSwift] 01.Fundamental / raw  (0) 2021.12.19

    댓글

Designed by Tistory.