본문 바로가기

공부/swift

[swift] RxSwift Operator(SugarAPI)

2020/09/14 - [공부/swift] - [swift] RxSwift 기본적인 사용법

위 링크에서 사용하던 코드들을 보면, 데이터를 생성하는 쪽이나, 받아오는쪽이나 너무 코드들이 길다.

이런 귀찮은 작업들을 줄여주는 것을 Sugar API라고 한다.

그것들에 대해 알아보자.

 

1. Just

"Hello World"를 보내는 코드를 기존과 같은 방식으로 만들어보자.

return Observable.create() { emitter in //Observable 생성
        emitter.onNext("Hello World") // Hello World전달
        emitter.onCompleted() // 데이터 전달 끝
        
        return Disposables.create() // disposable 생성 후 전달
    }

위와같이 여러줄을 써야할 것이다.

겨우 "Hello World" 전달하고 싶은데 너무 많은 작업을 해야한다.

그럴때 사용하는 것이 Just다.

return Observable.just("Hello World")

한줄로 표현할 수 있다.

 

2. From

만약에 "Hello World"를 보내고 싶은데 "Hello"따로 "World"따로 보내고 싶으면 어떻게 해야할까?

Just를 사용한다면 배열로 묶어서 보내야할 것이다.

이럴때는 From을 사용한다.

return Observable.just(["Hello", "World"])

배열로 묶어서 하나씩 내려보낼 수 있다.

swift는 다른 타입도 배열로 묶을 수 있으므로 아래와 같은 코드도 동작한다.

return Observable.just(["Hello", "World", 4])

 

3. subscribe쪽 간결하게

현재 subscirbe쪽 코드를 보면 아래와 같다.

    downloadJson(MEMBER_LIST_URL)
            .subscribe { event in
                switch event {
                case .next(let json):
                    DispatchQueue.main.async {
                        self.editView.text = json
                        self.setVisibleWithAnimation(self.activityIndicator, false)
                    }
                case .completed:
                    break
                case .error:
                    break
                }
        }.disposed(by: disposeBag)

처리는 Next쪽만 하는데 쓸데 없이 completed랑 error랑 여러가지가 있고 굳이 또 event를 받아서 분기하고 있다.

이런 경우 아래처럼 사용할 수 있다.

    downloadJson(MEMBER_LIST_URL)
            .subscribe(onNext: { json in
                
            }, onError: { err in
                
            }, onCompleted: {
                
            })
        .disposed(by: disposeBag)

여기서 ,로 구분되어 있는 onError나 onCompleted는 처리 안할거면 지워도 된다.

    @IBAction func onLoad(_ sender: UIButton) {
        editView.text = ""
        self.setVisibleWithAnimation(self.activityIndicator, true)
        downloadJson(MEMBER_LIST_URL)
            .subscribe(onNext: { json in
                DispatchQueue.main.async {
                    self.editView.text = json
                    self.setVisibleWithAnimation(self.activityIndicator, false)
                }
            })
        .disposed(by: disposeBag)
    }

이렇게 간결하게 사용할 수 있다.

 

4. ObserveOn

아까 DispatchQueue.main.async를 들여쓰기 귀찮으니 아래와 같이 바꿔도 동일하게 동작한다.

    @IBAction func onLoad(_ sender: UIButton) {
        editView.text = ""
        self.setVisibleWithAnimation(self.activityIndicator, true)
        downloadJson(MEMBER_LIST_URL)
            .observeOn(MainScheduler.instance) // 메인스레드
            .subscribe(onNext: { json in
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
            })
            .disposed(by: disposeBag)
    }

 

5. Map

Observable이 배출한 것에 함수를 적용하는 것이다.

그래서 전달되는 값들을 받아 변환할 수 있다.

간단하게 json이 전달되니 이를 카운트로 바꿔서 subscribe에 내려보자.

    @IBAction func onLoad(_ sender: UIButton) {
        editView.text = ""
        self.setVisibleWithAnimation(self.activityIndicator, true)
        downloadJson(MEMBER_LIST_URL)
            .map { json in json?.count ?? 0 } // count가 내려간다
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { json in // Int값으로 내려온다.
                self.editView.text = String(json) // Int값이기 때문에 Text에 쓰려면 String으로 변환해준다
                self.setVisibleWithAnimation(self.activityIndicator, false)
            })
            .disposed(by: disposeBag)
    }

 

6. Filter

Observable이 배출한 것에 조건을 만족하는 것들만 전달된다.

아까 count를 전달할때 0이 아닌지를 filter를 통해 체크하고 String으로 변환하는건 다시 map으로 해보겠다.

    @IBAction func onLoad(_ sender: UIButton) {
        editView.text = ""
        self.setVisibleWithAnimation(self.activityIndicator, true)
        downloadJson(MEMBER_LIST_URL)
            .map { json in json?.count ?? 0 }
            .filter { cnt in cnt > 0 } // 0보다 큰 경우만 전달
            .map{"\($0)"} // String으로 변환
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { json in // string으로 전달되기 때문에
                self.editView.text = json // 그냥 text에 넣으면 된다.
                self.setVisibleWithAnimation(self.activityIndicator, false)
            })
            .disposed(by: disposeBag)
    }

 

7. Drive

UI관련 코드들은 아래와 같이 항상 MainThread에서 동작한다.

    viewModel.itemsCount
            .map {"\($0)"}
            .observeOn(MainScheduler.instance) // 메인스레드
            .bind(to: itemCountLabel.rx.text)
            .disposed(by: disposeBag)

또한 이 stream은 에러가 나면 끊켜서 더 이상 작동하지 않는다.

    viewModel.itemsCount
            .map {"\($0)"}
            .catchErrorJustReturn("") // 에러가 나면 ""를 넘겨줌
            .observeOn(MainScheduler.instance)
            .bind(to: itemCountLabel.rx.text)
            .disposed(by: disposeBag)

그럴때는 위와 같이 catchErrorJustReturn을 쓴다.

근데 이걸 매번 써주기 귀찮으니 Drive를 사용한다.

    viewModel.itemsCount
            .map {"\($0)"}
            .asDriver(onErrorJustReturn: "")
            .drive(itemCountLabel.rx.text)
            .disposed(by: disposeBag)

 

8. Relay

drive와 비슷하게 에러가 나도 끊어지지 않는 Subject를 relay라고 한다.

    //lazy var menuObservable = BehaviorSubject<[Menu]>(value: [])
    lazy var menuObservable = BehaviorRelay<[Menu]>(value: [])

그냥 원래 Subject자리를 Relay로 고치기만 하면 된다.

그래서 Subject랑 똑같은데 에러가 나도 stream이 끊어지지 않는다.

	//self?.menuObservable.onNext(self?.menus ?? [])
                self?.menuObservable.accept(self?.menus ?? [])

에러가 없으니 onNext로 받지 않고 accept으로 받는다.

에러처리가 없기 때문에 onComplete나 onError가 없다.

 

이렇게 제공해주는 Sugar들을 Operator라고 하고 아래 사이트에서 여러 Operator와 동작과 설명을 볼 수 있다.

reactivex.io/documentation/ko/operators.html

 

ReactiveX - Operators

연산자 소개 ReactiveX를 지원하는 언어 별 구현체들은 다양한 연산자들을 제공하는데, 이 중에는 공통적으로 제공되는 연산자도 있지만 반대로 특정 구현체에서만 제공하는 연산자들도 존재한다

reactivex.io

더 많은 예제는 아래 링크에 좀 더 써뒀다.

2020/09/06 - [공부/swift] - [swift] RxSwift

 

[swift] RxSwift

1. Rx란? Reactive X. 홈페이지에는 이렇게 적혀있다. An API for asynchronous programming with observable streams Async한 프로그래밍한다 observe가 가능한 stream들로. 뭔소린지 하나도 모르겠으니 예제랑..

life-shelter.tistory.com

 

'공부 > swift' 카테고리의 다른 글

[RxSwift] JSON파싱해서 테이블뷰에 뿌리기  (2) 2020.09.17
[swift] RxSwift 기본적인 사용법  (0) 2020.09.14
[swift] RxCocoa  (0) 2020.09.09
[swift] RxSwift  (0) 2020.09.06