티스토리 뷰

Mobile/iOS

RxSwift 시작하기

out of coding 2018. 3. 24. 14:22

제가 Reactive를 알게 된게 1년정도 된것 같네요.

그렇게 알게 되고 이것저것 하다보니 이것에 대한 매력을 좀 느끼고 있는 편입니다.

그 당시에 MVVM 패턴으로 개발을 시작하였고 현재 진행하는 프로젝트도 이것으로 진행중입니다.

 

대략 이곳저곳에서 이것을 사용하는 방법에 대해서 슬며시 올려지고는 있는것 같은데

아직은 활성화가 안되어 있는것 같기도 하네요.

 

우리의 목적은 다음과 같습니다.

UIViewController에서 로직을 모두 제거하자.

 

이유는 그렇습니다. 로직을 자꾸 추가를 하다보니 로직에 로직을 뷰에 뷰를 맞추는 작업이 혼합이 되어 있는 코드가 발생하게 되고요.

이렇게 개발을 하다보니 뷰만 슬쩍 수정하면 되는 문제인데, 로직이 엉켜서 아무것도 못하는 상황이 발생하게 됩니다.

 

일단 저는 이전에는 이러한 부분들을 별도의 Controller를 만들어서 개발을 하였습니다. ViewModel이라는 이름만 사용을 하지 않았을 뿐이지 동일한 개념이었죠.

 

지금은 View의 Model이니 ViewModel, 그리고 그 안에서 사용할 Data들은 ViewData 등의 이름을 사용합니다. 이건 뭐 다들 좋아하시는 이름으로 사용하시는게 좋을것 같네요...

 

일단 말이 길어졌네요.

 

RxSwfit를 이용하게 되면, MVVM 부분의 value change에 대해서 구독하여서 View의 상태만 변경을 하여 주면 아주 깔끔한 소스가 나오게 되겠죠.

 

일단 Pod으로 RxSwfit를 가져오도록 합니다.

저의 경우에는 RxSwift, RxCocoa를 함께 사용합니다. 아주 간단하게는요.

cocoa pod에 가보면 RxSwfitExt도 있고 많이 있으니 사용하고 싶으신 분들은 사용하시길

저는 그냥 Ext에서는 기능만 보고 필요한 부분들만 제가 만들어서 사용하는 편입니다.

예를 들어 filterNil() 같은 function은 기본 기능에서는 제공을 하지 않습니다.

 

1
2
3
  # RX Core
  pod 'RxSwift'
  pod 'RxCocoa'
cs

 

위는 주석이니까 그냥 아래만 추가해도 됩니다.

저는 Swift버전도 4라서 위의 것을 그대로 사용하면 되는데요. Swift가 3버전일 경우에는 공식 사이트에서 3대로 버전을 명시해주시면 됩니다.

 

간단하게 ViewModel에 value를 전달하고 data를 가져오는 예제를 볼까 합니다.

아주 간단합니다. map 만 사용하는 예제일거니까요.

 

ViewModel의 Input을 정의합니다.

 

1
2
3
4
5
6
7
8
9
let timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
 
timer
    .bind(to: viewModel.input.number)
    .disposed(by: disposeBag)
        
button.rx.tap
    .subscribe(onNext: viewModel.didClick)
    .disposed(by: disposeBag)
cs
 

간략하게 Timer를 만들어서 1초에 한번씩 동작하도록 합니다. Int로 Observable을 만들었기 때문에 0부터 값이 시작될것입니다.

 

그 아래 라인의 button.rx.tap은 rx방식으로 button의 click을 받아들여서 viewModel의 didClick() function을 호출하는 부분입니다.

 

ViewModel의 Output을 정의합니다.

 

1
2
3
4
5
6
7
8
9
viewModel.output.number
    .drive(button.rx.title(for: .normal))
    .disposed(by: disposeBag)
        
viewModel.output.date
    .subscribe(onNext: { [weak self] in
        guard let `self` = self, let date = $0 else { return }
        self.printer(date)
    }).disposed(by: disposeBag)
cs

 

웬만하면 저는 위와 같이 개발을 하지 않습니다. 더 간략하게 할수 있으니까요.

그렇지만 일반 기본적은 RxSwift에서 사용할 경우에 대해서 설명중이기 때문에 이렇게 적었네요.

 

위의 경우를 보면 ViewController의 View가 변해야하는 부분에 대해서만 Listen하고 있습니다.

 

이전에 프로그램을 생각해보면 View의 변경지점을 만들기 위해서 다음과 같이 개발하였겠죠.

 

1
2
3
4
5
if text.isEmpty {
    self.printer("empty")
else {
    self.printer(text)
}
cs

 

아주 간략한 뷰의 구성이면 위처럼 짜는게 맞습니다. 저도 부정하지 않는 부분입니다.

 

그런데 뷰가 복잡해지게 되면 위와 같이 개발을 하면 나중에 유지보수하는 분은 지옥에 가는꼴이 되겠죠.

 

ViewModel 입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct MainViewModel {
    
    let input: Input
    let output: Output
    
    init() {
        input = Input()
        output = Output(
            number: input.number.asDriver()
                .filter { $0 >= 0 }.map { String($0) },
            date: input.number.asObservable()
                .map { _ in Date().description }
        )
    }
}
 
extension MainViewModel {
    
    func didClick() {
        print(">>> on Click")
    }
}
 
extension MainViewModel {
    struct Input {
        let number = Variable<Int>(-1)
    }
    
    struct Output {
        let number: Driver<String>
        let date: Observable<String?>
    }
}
cs

 

View의 초기값과 이것을 이용하여 변화되는 값들을 전달할지 말지의 여부 ( filter ) 와 이것을 가지고 값을 변경하여서 화면에 보여줄수 있도록 전달하는 부분 ( map )을 이용하였습니다.

 

이정도는 설명을 하지 않아도 개발자적으로 알수 있으실거라고 생각합니다.

 

궁금하시면... 댓글로 남겨주세요. 답변드릴게요.

 

일단 오늘 설명드린 부분들만 하실수 있어도 MVVM으로 개발을 하는 방법에 대해서는 이해가 되었을 것이라고 생각되네요.

 

참고로 저는 이것들을 더 함축적으로 줄여서 만들고 있는데요. 그것까지는 설명드리기 어려울것 같네요.

 

전체 소스는 github에 올려두었습니다.

 

https://github.com/outofcode-example/RxSwift-RxMVVM_Basic

 

outofcode-example/RxSwift-RxMVVM_Basic

RxSwift 프로젝트 기본. Contribute to outofcode-example/RxSwift-RxMVVM_Basic development by creating an account on GitHub.

github.com

 

모르겠는 부분은 댓글 달아주세요.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함