Home RxSwift 개념잡기
Post
Cancel

RxSwift 개념잡기

RxSwift 개념잡기

  • 목표: 스트림 기반 기본 동작에 대한 이해

개념

  • 절차형 프로그래밍의 한계: 복잡도가 올라갈 수록 유지보수하기 어려워 진다.
  • 스레드에 따른 결과가 달라진다.
  • 그래서 콜백/리스너 개념을 도입

콜백/리스너의 단점

  1. 예측 불가능한 순서: 이벤트가 도착하는 순서는 Observer를 등록한 순서에 따라 변경될 수 있음
  2. 첫 번째 이벤트 소실
  3. 스레드 문제
  4. 콜백 누수: Observer를 해제하는 것을 잊으면 메모리 누수가 발생

FRP (functional reactive programming)

  • 선언적 프로그래밍: 개념적으로 어떤 특성이 필요한지 정의. 순차적으로 어떻게 이루어져야 하는지 나열하는 것 보다 효율적.
  • Subject를 오직 정보의 근원으로만 봐야한다. 결코 “이제 여기서 코드가 이 값을 푸시한다”라고 말해서는 안된다. 마음 속에서 그런 생각을 아예 없애버려라.
  • FRP를 사용하려면 개념적인 수준에서 생각하기 위해 노력하라. 여러 요소 간 상호작용의 매커니즘이 아니라 그들 사이의 관계 수준에서 머물러라.

참조 투명성

  • map에 전달하는 함수가 반드시 참조 투명성(referential transparency)을 보장해야 한다.
  • 외부에서 볼 수 있는 변수의 상태를 변경해서는 안된다.
  • 함수 호출과 호출 사이에 지속되는 상태를 유지해서는 안된다.
  • 짧게 말해, 참수는 반환값을 제외한 외부 효과를 만들어서는 안되며, 함수의 반환값이 외부 상태에 의해 영향 받는 일도 없어야 한다.

참고: 함수형 반응형 프로그래밍

Observable

  • hot observable: 생성되자마자 데이터를 흘려보내는 것
  • cold observable: subscribe 된 다음 데이터를 흘려보내는 것
1
2
3
4
5
6
7
8
9
10
11
12
13
enum Event<Element>  {
    case next(Element)      // next element of a sequence
    case error(Swift.Error) // sequence failed with error
    case completed          // sequence terminated successfully
}

class Observable<Element> {
    func subscribe(_ observer: Observer<Element>) -> Disposable
}

protocol ObserverType {
    func on(_ event: Event<Element>)
}

Subject

Observable + Observer

  • BehaviorSubject: 구독하면 가장 최근 값 가져오기, 구독 이후 반환 값 가져오기
  • PublishSubject: 구독 이후 반환 값 가져오기
  • ReplaySubject: 구독 이전 값(버퍼 사이즈에 따라 다름), 구독 이후 값 가져오기

상태 관리

Rx에서는 상태를 관리하기 위한 메서드 3개와 클래스 1개를 제공한다.

  • scan(): 지정한 초기값으로부터 상태값 누적
  • withLastestFrom(): 가장 최근의 observable 값 가져오기
  • combineLatest()
  • BehaviorSubject

예제

UIButton

https://academy.realm.io/kr/posts/how-to-use-rxswift-with-simple-examples-ios-techtalk/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func setup() {
    let reloadButton = UIButton()
    reloadButton.rx.tap
        .debounce(0.3, scheduler: MainScheduler.instance)
        .do(onNext: {
            print("reload button clicked")
        })
        .bind(onNext: reload)
        .disposed(by: disposeBag)
}

func reload() {

}

UISearchBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let searchResults = searchBar.rx.text.orEmpty
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[Repository]> in
        if query.isEmpty {
            return .just([])
        }
        return searchGitHub(query)
            .catchErrorJustReturn([])
    }
    .observeOn(MainScheduler.instance)

searchResults
    .bind(to: tableView.rx.items(cellIdentifier: "Cell")) {
        (index, repository: Repository, cell) in
        cell.textLabel?.text = repository.name
        cell.detailTextLabel?.text = repository.url
    }
    .disposed(by: disposeBag)
This post is licensed under CC BY 4.0 by the author.

RxDataSource 예제를 보며 RxSwift 익히기

Stack View subview hidden시 constraints 문제