본문 바로가기

IOS Swift

RxSwift 정리

목차

  • RxSwift 란?
  • RxSwift 사용하는 이유
  • RxSwift 구성 3요소
  • Hot Observable, Cold Observable

RxSwift란?

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

비동기와 이벤트 처리를 observable한 시퀀스로 만들기 위한 라이브러리

Observable
.map
.subscribe
.dispose
.
.~~

 

 

RxSwift 사용 이유

콜백 지옥에서 벗어나 비동기처리를 함수의 반환값으로 만들어 읽기 쉬운 코드로 만듭니다.

RxSwift, Combine, Promisekit 같은 비동기 라이브러리를 사용하지 않는다면 escaping closure를 사용해야 하기 때문에 코드의 흐름 파악이 힘듭니다.

class SeverAPI {

    // 비동기 함수라고 생각하시면 됩니다.
    func downLoad(completion: @escaping (String) -> Void) {
        completion("1")
    }
    
    func downLoad2(completion: @escaping (String) -> Void) {
        completion("2")
    }
    
    func downLoad3(completion: @escaping (String) -> Void) {
        completion("3")
    }
}

class VC {
    
    let serverAPI = SeverAPI()
    
    func getData() {
        serverAPI.downLoad { str in
            print(str)
            self.serverAPI.downLoad2 { str2 in
                print(str2)
                self.serverAPI.downLoad3 { str3 in
                    print(str3)
                }
            }
        }
    }
    
	// 1
	// 2
	// 3
}

위의 코드를 보면 downLoad1을 완료하고 그 다음 downLoad2를하고 downLoad3을 하는 코드가 있습니다. 보시면 계속 depth가 늘어나서 보기가 힘든 문제점이 있습니다.

이것을 RxSwift를 사용하여 고쳐보겠습니다.

class SeverAPI {
    
    func downLoad() -> Observable<String> {
        return Observable.just("1")
    }
    
    func downLoad2() -> Observable<String> {
        return Observable.just("2")
    }
    
    func downLoad3() -> Observable<String> {
        return Observable.just("3")
    }
}

class VC {
    
    let serverAPI = SeverAPI()
    
    func getData() {
        Observable.concat([serverAPI.downLoad(), serverAPI.downLoad2(), serverAPI.downLoad3()])
            .subscribe(onNext: { data in
                print(data) 
        })
    }
    
	// 1
	// 2
	// 3
}

훨씬 더 보기 편해졌죠? ㅎ.. concat 연산자란 Observable 타입 여러개를 결합해주는 연산자 입니다.

위의 코드를 그림으로 그려보면 Observable 타입 3개가 있고 concat으로 결합하고 한개씩 출력하게 됩니다.

 

다른 장점으로는 ReactiveX는 다중 언어로 구현되어 있으므로 하나의 언어로 알아두면 다른 언어를 봤을 때 금방 이해할 수 있는 장점도 있습니다.

예를들면 안드로이드에서도 ReactiveX를 사용한다거나 안드로이드,iOS 둘다 개발해야 한다거나 이럴 때 좋을 것 같습니다.

 

RxSwift 구성 3요소

  1. Observable : Observer가 구독 할 수 있는 타입
  2. Operators : Observable 타입을 생성, 변환, 오류처리, 결합, 필터링 할 수 있게 해줌
  3. Scheduler : 어떤 스레드로 처리할 것인지 

 

1. Observable

Observable의 변화를 감지하여 Observer가 onNext, onError, onCompleted, onDisposed 에서 알맞는 로직을 실행합니다.

Subscribe 메서드를 통해 Observer와 Observable을 연결합니다.

struct Model {
    var age: Int
}

class SeverAPI {
    func download() -> Observable<Model> {
        
        // Observable 타입 생성
        
        return Observable.create { emitter in
            emitter.onNext(Model(age: 10))
            
            return Disposables.create { }
        }
    }
}

// Subscribe(구독) - Observer와 Obsevarble을 연결

class VC {
    let serverAPI = SeverAPI()
    var disposeBag = DisposeBag()
    
    func bind() {
        serverAPI.download()
        .subscribe(onNext: { _ in
        // 정상 구독 될 때
        }, onError: { _ in
        // error 날 때
        }, onCompleted: {
        // 완료 될 때
        }, onDisposed: {
        // dispose(취소) 될 때
        }).disposed(by: disposeBag)
    }
}

 

2. Operators

생성

func download() -> Observable<Model> {
        
        // Observable 타입 생성
        
        return Observable.create { emitter in
            emitter.onNext(Model(age: 10))
            
            return Disposables.create { }
        }
    }
}

변환

class VC {
    let serverAPI = SeverAPI()
    var disposeBag = DisposeBag()
    
    func bind() {
        serverAPI.download()
        // Model -> String 타입 변환
        .map({ model in
            String(model.age)
        })
        .subscribe(onNext: { modelAgeStr in
        // 정상 구독 할 때
        }, onError: { _ in
        // error 날 때
        }, onCompleted: {
        // 완료 될 때
        }, onDisposed: {
        // dispose 될 때
        }).disposed(by: disposeBag)
    }
}

 

3. Scheduler

어떤 스레드로 처리할 것인지

RxSwift는 다음과 같은 내장 스케줄러가 있습니다.

  • CurrentThreadSchecduler(직렬) : 현재 작동하고 있는 스레드에 작업 단위를 예약함. 연산자의 기본 스케줄러 입니다.
  • MainScheduler(직렬) : 메인스레드에서 작업해야 할 때 사용. 주로 UI 처리할 때 사용 합니다.
  • SerialDispatchQueueScheduler(직렬): 특정 디스패치 큐를 사용할 때 이 스케줄러를 사용 합니다.
  • ConcurrentDispatchQueueScheduler(동시) : 특정 디스패치 큐를 사용할 때 이 스케줄러를 사용. 주로 백그라운드 작업
  • OperationQueueScheduler(동시 스케줄러) : NSOperationQueue를 사용하려 할 때 이 스케줄러를 사용, 주로 백그라운드 작업
class VC {
    let serverAPI = SeverAPI()
    var disposeBag = DisposeBag()
    
    func bind() {
        serverAPI.download()
        // .observe(on: MainScheduler.instance) 아래의 연산자들은 모두 MainScheduler.instance 가 처리함
        .observe(on: MainScheduler.instance)
        .map({ model in
            String(model.age)
        })
        .subscribe(onNext: { modelAgeStr in
        // 정상 구독 할 때
        }, onError: { _ in
        // error 날 때
        }, onCompleted: {
        // 완료 될 때
        }, onDisposed: {
        // dispose 될 때
        }).disposed(by: disposeBag)
    }
}

 

제가 공부하면서 알아간 것 위주로 정리를 했습니다. 공식홈페이지에 가면 더 다양한 장점과 더 많은 연산자들, 사용하는 이유 등등 많은 것들이 잘 작성되어 있으므로  참고하시기 바랍니다.


Hot Observable vs Cold Observable

Observable은 연속된 항목들을 언제 배출할까? 에 대한 질문과도 같습니다.

Hot Observable과 Cold Observable이 나뉘는 기준??

  • 구독 시점과 상관없이 항목을 배출한다. -> Hot Observable
  • 구독한 시점에 항목을 배출한다. -> Cold Observable

 

Hot Observable

  • 구독자가 구독하지 않아도, 데이터를 방출
  • 구독자가 구독하지 않아도 데이터를 방출하기 때문에, 구독자가 구독하기 이전에 발행된 데이터는 구독자가 데이터를 받을 수 없음

 

“뜨거운” Observable은 생성되자 마자 항목들을 배출하기도 하기 때문에, 이 Observable을 구독하는 옵저버들은 어떤 경우에는 항목들이 배출되는 중간부터 Observable을 구독할 수 있다.

publish 메소드를 사용해서 ConnectableObservable을 만들 수 있다 .

extension ObservableType {

    /**
    Returns a connectable observable sequence that shares a single subscription to the underlying sequence.

    This operator is a specialization of `multicast` using a `PublishSubject`.

    - seealso: [publish operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html)

    - returns: A connectable observable sequence that shares a single subscription to the underlying sequence.
    */
    public func publish() -> ConnectableObservable<E> {
        return self.multicast { PublishSubject() }
    }
}

ConnectableObservable 설명

“Connectable” Observable. Such an Observable does not begin emitting items until its Connect method is called, whether or not any observers have subscribed to it.

 ConnectableObservable는 구독 시점에 상관 없이, subscribe 이후의 항목을 배출하므로 Hot Observable 이다. 

ConnectableObservable에서 Connect 연산자가 적용될 때만 항목을 방출한다.

        let observable = Observable<Int>
            .interval(.seconds(1), scheduler: MainScheduler.instance)
            .publish()
            
        observable.connect()
        
        observable.subscribe(onNext: { second in
            print("observer 1: ", second)
        })
        
        timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { _ in
            observable.subscribe(onNext: { second in
                print("observer 2: ", second)
            })
        })
        
//    observer 1:  0
//    observer 1:  1
//    observer 1:  2
//    observer 1:  3
//    observer 2:  3 -> 중간부터 구독
//    observer 1:  4
//    observer 2:  4
//    observer 1:  5
//    observer 2:  5
//    observer 1:  6
//    observer 2:  6
//    observer 1:  7
//    observer 2:  7

Cold Observable

  • 구독자가 구독하는 시점에 데이터 발행
  • 구독자가 구독하는 시점에 데이터 발행을 시작하기 때문에, 구독자는 발행 처음부터 끝까지 데이터를 보장 받음

 

 “차가운” Observable은 옵저버가 구독할 때 까지 항목을 배출하지 않기 때문에 이 Observable을 구독하는 옵저버는 Observable이 배출하는 항목 전체를 구독할 수 있도록 보장 받는다.

 

        let observable = Observable<Int>
            .interval(.seconds(1), scheduler: MainScheduler.instance)
        
        observable.subscribe(onNext: { second in
            print("observer 1: ", second)
        })
        
        timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { _ in
            observable.subscribe(onNext: { second in
                print("observer 2: ", second)
            })
        })
        
//    observer 1:  0
//    observer 1:  1
//    observer 1:  2
//    observer 1:  3
//    observer 2:  0 -> 처음부터 끝까지 항목 배출 보장
//    observer 1:  4
//    observer 2:  1
//    observer 1:  5
//    observer 2:  2
//    observer 1:  6
//    observer 2:  3
//    observer 1:  7
//    observer 2:  4
//    observer 1:  8
//    observer 2:  5

 

정리

Hot Observable

  • 구독 시점으로부터 발행하는 값을 받는 걸 기본으로 한다.
  • 마우스 이벤트, 키보드 이벤트, 시스템 이벤트 등이 주로 사용된다.

Cold Observable

  • 일반적으로 서버에 데이터를 요청할 때 사용된다.
  • 처음부터 발행하는 것을 기본으로 한다.

 

 


참고 

반응형

'IOS Swift' 카테고리의 다른 글

Fastlane Match로 Signing 하기(feat. iOS)  (0) 2022.04.22
Fastlane 배포 자동화 적용(feat. iOS)  (0) 2022.04.21
iOS - 이벤트 처리 흐름  (0) 2022.02.11
App Store 개발자 등록하기  (0) 2021.12.20
아스키코드와 유니코드  (0) 2021.11.20