Today 12/22
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의 생명주기
let ob = downloadJson()
이렇게 Create만 하고 .subscribe하지 않으면 주기가 실행되지 않는다.
.debug()
를 이용해서 log로 생명주기를 관찰할 수 있다.