subscribe( on: (Event<type>) -> Void)

Rx의 여러 오퍼레이터로 이루어진 스트림의 마지막엔 subscribe()을 통해 가공된 데이터를 확인하게 된다. 여기서 Event<타입>형태의 데이터가 들어오게 되고, Event는 next, error, completed 3가지의 값을 가질 수 있다. 

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next:
            break
        case .error:
            break
        case .completed:
            break
        }
    })
    .disposed(by: disposeBag)

next

스트림에서 넘어온 자료가 있는 경우, next에 해당한다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")    //자료 출력
            break
        case .error(let err):
            break
        case .completed:
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// next: In
// next: 4
// next: Hours

error

스트림에서 에러가 발생한 경우, error의 값을 가지게 된다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .single()    //하나의 데이터만 들어와야함.
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")
            break
        case .error(let err):
            print("err: \(err.localizedDescription)")
            break
        case .completed:
            print("completed")
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// err: The operation couldn’t be completed. (RxSwift.RxError error 5.)

 

single()오퍼레이터의 경우 하나의 데이터만 입력을 받아야 하는데, 스트림의 앞쪽에 위치한 from() 오퍼레이터의 경우 이벤트가 여러번 발생되기 때문에 에러가 발생하게된다. 이렇게 고의로 에러를 발생시켜보면 에러의 내용이 출력되는 모습을 확인할 수 있고, completed 될 수 없다고 나온다. 즉 에러가 발생하면 completed 상태에 들어가지 않고 스트림은 마무리된다.


completed

해당 스트림이 에러없이 모든 이벤트들을 내보냈을경우 completed 값을 가지게 된다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")
            break
        case .error(let err):
            print("err: \(err.localizedDescription)")
            break
        case .completed:
            print("completed")
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// next: In
// next: 4
// next: Hours
// completed

해당 코드를 실행하게되면 from에 의해 생성된 4개의 자료들이 출력되고, 마지막으로 "completed"가 출력되는 모습을 볼 수 있다. 

즉 에러가 발생하지 않는다면, 발생되는 모든 이벤트를 처리하고 마지막으로 completed 상태가 되면서 마무리가 된다.


subscribe(onNext:  , onError: , onCompleted:  , onDisposed:  )

subscibe(_ on: )의 경우, switch문을 통해 event의 모든 조건에 대한 처리를 해주어야 했다면, subscribe(onNext:  ,onError:  ,onCompleted:  ,onDisposed:  )의 경우 원하는 경우의 분기만 골라서 작성이 가능하게 해주는 오퍼레이터이다.

또 다른 차이점은 onDisposed가 추가되었다는 점이다. 

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe(onNext: { str in
        print(str)
    })
    .disposed(by: disposeBag)
    
// RxSwift
// In
// 4
// Hours

모든 경우의 값에 대해 작성해야 하는 subscribe( _ on:  )과 달리 필요한 부분만 이벤트 처리하여 더 간결한 코드 작성이 가능하다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .single()
    .subscribe(onNext: { str in
        print(str)
    }, onError: { err in
        print(err.localizedDescription)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })
    .disposed(by: disposeBag)
    
// RxSwift
// The operation couldn’t be completed. (RxSwift.RxError error 5.)
// disposed
Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe(onNext: { str in
        print(str)
    }, onError: { err in
        print(err.localizedDescription)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })
    .disposed(by: disposeBag)
    
// RxSwift
// In
// 4
// Hours
// completed
// disposed

예제 코드에서 알 수 있듯, 스트림은 마지막으로 disposed 되면서 마무리된다. 강의에서 말하길, 많은 개발자들이 흔히 하는 실수로 프로세스의 종료처리를 onCompleted 부분에서 작성하게 되는데, 이렇게 되면 에러 발생시에 onCompleted 상태에 들어가지 않게 되어 프로세스의 종료처리가 일어나지 않게 된다. 따라서 프로세스의 마무리 처리를 하려면 onDisposed 부분에서 작성해야 한다.

 

[참고영상]

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 7

'iOS > RxSwift' 카테고리의 다른 글

scheduler  (0) 2022.08.31
operator  (0) 2022.07.21
subscribe, dispose  (0) 2022.06.15
Observable stream  (0) 2022.06.08
ReactiveX  (0) 2022.05.30

 

sync, async

Rx에서 가장 중요한 개념인 Observable에 대해 이야기하기전에 우선 동기(sync)와 비동기(async) 프로그래밍에 대해 간단하게 설명하고자 한다. 

private func loadImage(from imageUrl: String) -> UIImage? {
        guard let url = URL(string: imageUrl) else { return nil }
        guard let data = try? Data(contentsOf: url) else { return nil }
        
        let image = UIImage(data: data)
        return image
}

 

여기 URL요청을 통해 이미지를 불러와 UIImage 형태로 리턴해주는 함수가 있다. 동기방식으로 처리할경우 이미지를 요청하고 요청에 대한 응답을 받아야만 다음처리가 가능해진다. 이에 반해 비동기방식은 요청을 하고난 후 응답을 받기 전에 다음 작업들을 이어서 처리하다가 요청에 대한 응답이 수신되면 응답에 대한 처리를 마저 하게되는 방식이다.  동기방식은 요청에 대한 응답이 올때 까지 대기. 비동기방식은 요청에 대한 응답을 대기하지 않고 다음작업들을 처리. 차이라고 보면 된다. 아래의 코드를 통해 두 방법의 차이를 확인해보자.

 

//동기방식
@IBAction func onLoadSync(_ sender: Any) {
    let image = loadImage(from: IMAGE_URL)
    imageView.image = image
}

//비동기방식
@IBAction func onLoadAsync(_ sender: Any) {
    DispatchQueue.global().async {
        let image = self.loadImage(from: self.IMAGE_URL)
        DispatchQueue.main.async {
            self.imageView.image = image
        }
    }
}

 

우측의 시간카운터를 보면 동기방식과 비동기방식의 차이를 확인할 수 있다. 

 

동기방식으로 요청을 처리할경우, 응답을 받을 때까지 아무런 작업을 할 수 없게된다. 이렇게 동기방식으로 작성된 앱은 사용자에게 큰 불편을 유발하게 된다. 하지만 위의 예시처럼 비동기 방식으로 작성하게되면 코드의 가독성이 떨어진다.

Observable stream

다음은 RxSwift로 작성된 비동기 코드를 보자.

var disposebag = DisposeBag()
@IBAction func onLoadRxAsync(_ sender: Any) {
    Observable.just(self.IMAGE_URL)
        .subscribe(on: ConcurrentDispatchQueueScheduler(qos: .default))
        .map{self.loadImage(from: $0)}
        .observe(on: MainScheduler.instance)
        .subscribe(onNext: { image in
            self.imageView.image = image
        })
        .disposed(by: disposebag)
}

처음 Rx코드를 보았을때 오히려 코드라인이 늘어나서 당황했다. 하지만 코드의 흐름을 보면 들여쓰기와 중괄호가 아닌 Observable에서 시작되는 메소드들로 흘러가는 모양을 볼 수 있고, 마지막 subscribe부분에서 UI처리를 하는 모습을 볼 수 있다. 이러한 형태가 바로 이전글에 Rx의 정의를 이해할 수 있는 좋은 예시이다.

An API for asynchronous programming
with "observable streams"

Observable에서 파생되는 stream(흐름)을 통한 비동기 프로그래밍 API. 즉 Observable에서 시작되는 반환값이 여러가지 Operator, Subject, Scheduler들로 이루어진 흐름을 통해 비동기 프로그래밍을 할 수 있는 API라고 할 수 있다. 이것이 Rx의 정의이다.

 

참고영상

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 2

'iOS > RxSwift' 카테고리의 다른 글

scheduler  (0) 2022.08.31
next, error, complete  (0) 2022.08.19
operator  (0) 2022.07.21
subscribe, dispose  (0) 2022.06.15
ReactiveX  (0) 2022.05.30

 

 

RxSwfit. 여러 개발 블로그를 돌아다니다보면, 기술스택에 RxSwift가 적혀있는 것을 심심치않게 확인해 볼 수 있다. 볼때마다 저건 무엇일까 라는 의문만 남긴채 지나갔지만, 이번기회에 유튜브에서 곰튀김님의 "RxSwift 4시간에 끝내기" 영상을 보면서 공부한 내용을 적으려고 한다. 

What is Rx?

RxSwift는 ReactiveX의 Swift버전이라고 생각하면 된다. 그렇다면 reactiveX는 무엇일까? 공식 홈페이지에 들어가면 바로 확인이 가능하다.

An API for asynchronous programming
with observable streams

API이다. 그것도 비동기 프로그래밍을 위한. 시각적인 스트림을 통해서. 처음에는 스트림이 무엇인지 몰라 감이 안잡혔지만, 어찌되었든 결론은 ReactiveX는 비동기 프로그래밍에 사용되는 API라는 것을 알게 되었다. 

ReactiveX(이하 Rx)는 MS에서 모종의 프로젝트에 적용하기 위해 개발이 되었고, Rx를 통해 프로젝트를 진행해보니 좋았다는 피드백을 통해 여러가지 언어들에 맞게 변형되어 사용되게 된다. 홈페이지에 보면 C, java, python부터 시작하여 현대에 쓰이는 많은 언어들을 지원하는 모습을 확인 할 수 있다.

What is in Rx?

Rx에는 크게 다섯가지 요소가 있다. Observable, Operators, Scheduler, Subject, Single 이다. 서론 글을 짧게 마치고, 각 요소들에 대한 설명을 다음 글부터 이어가도록 하겠다.

 

참고영상

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 1

'iOS > RxSwift' 카테고리의 다른 글

scheduler  (0) 2022.08.31
next, error, complete  (0) 2022.08.19
operator  (0) 2022.07.21
subscribe, dispose  (0) 2022.06.15
Observable stream  (0) 2022.06.08

+ Recent posts