-
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>
에서Base
가UIButton
인 것에 대한 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