RxSwift 4시간 끝내기 - Observable

Choong Won, Seo·2021년 12월 22일
0

RxSwift

목록 보기
1/2
post-thumbnail

Today 12/22

Observable

ReactiveX - 마이크로소프트에서 처음 만들었다. 적용법이 비슷해서 RxSwift를 알면 다른 언어들에서도 쉽게 응용이 가능하다.

DispatchQueue와 Escaping Handler를 통한 Swift 내에서의 비동기(async) 구동 방식은 대략 이렇다.

func downloadJson(listUrl: String, downloadJsonHandler: @escaping (String?) -> ()) {
        DispatchQueue.global().async {
            let url = URL(string: listUrl)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                downloadJsonHandler(json)
            }
        }
    }

@IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)
        downloadJson(listUrl: MEMBER_LIST_URL) { json in
            self.editView.text = json
            self.setVisibleWithAnimation(self.activityIndicator, false)
        }
    }

하지만 이런 식으로 구현을 했을 때 중첩된 코드들, 계속된 들여쓰기는 코드 가독성을 떨어뜨린다.

그래서 Rx의 핵심은 downloadJsonHandler: @escaping (String?) -> () 를 그냥 String?으로 받아버릴 수는 없을까?

func downloadJson(listUrl: String) -> Observable<String?> {
				// 1. 비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법
        return Observable.create() { f in
            DispatchQueue.global().async {
                let url = URL(string: listUrl)!
                let data = try! Data(contentsOf: url)
                let json = String(data: data, encoding: .utf8)
                DispatchQueue.main.async {
                    f.onNext(json)
										**f.onCompleted()**
                }
            }
            return Disposables.create()
        }
    }

@IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)
        
				// 2. Observable로 오는 데이터를 받아서 처리하는 방법
        downloadJson(listUrl: MEMBER_LIST_URL)
            .subscribe { event in
                switch event {
                case let .next(json):
                    self.editView.text = json
                    self.setVisibleWithAnimation(self.activityIndicator, false)
                case .completed:
                    break
                case .error:
                    break
                }
            }
    }

return Disposables.create().dispose()를 통해서 작업을 취소시켜줄 수도 있다.

f.onCompleted()를 넣어준 이유는 순환참조 해결을 위해서이다.

f.onNext()는 한 번만 불릴 필요는 없다. 여러 번 불러서 여러 데이터를 전달하는 것도 가능하다.

URLSession을 이용한 방식

func downloadJson(_ url: String) -> Observable<String?> {
        return Observable.create() { emitter in
            let url = URL(string: url)!
            let task = URLSession.shared.dataTask(with: url) { (data, _, err) in
                guard err == nil else {
                    emitter.onError(err!)
                    return
                }
                if let dat = data, let json = String(data: dat, encoding: .utf8) {
                    emitter.onNext(json)
                }
                emitter.onCompleted()
            }
            task.resume()
            return Disposables.create() {
                task.cancel()
            }
        }
    }

@IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)
        
        downloadJson(MEMBER_LIST_URL)
						.debug()
            .subscribe { event in
                switch event {
                case let .next(json):
										DispatchQueue.main.async {
                        self.editView.text = json
                        self.setVisibleWithAnimation(self.activityIndicator, false)
                    }
                case .completed:
                    break
                case .error:
                    break
                }
            }
    }

Observable의 생명주기

  1. Create
  2. Subscribe
  3. onNext
  4. onCompleted / onError
  5. Disposed

let ob = downloadJson() 이렇게 Create만 하고 .subscribe하지 않으면 주기가 실행되지 않는다.

.debug()를 이용해서 log로 생명주기를 관찰할 수 있다.

profile
UXUI Design Based IOS Developer

0개의 댓글