RxSwift가 생긴 이유가 뭘까
swift로 비동기 로직을 구현할 때 한번쯤은 아래와 같은 복잡한 코드를 경험해봤을 것이다.
비동기적 방법으로 통신을 하기 때문에 뒤늦게 그 결과로 가져오게 될 json을 외부로 전달할 방법이 없다.
때문에 completionHandler escaping 클로저를 사용하게 되고, 그 결과 코드가 복잡해지는 것이다.
func downloadJson(_ url: String, _ completion: @escaping (String?) -> Void) {
DispatchQueue.global().async {
// some code
DispatchQueue.main.async {
completion(json)
}
}
}
func onLoad() {
editView.text = ""
downloadJson(MEMBER_LIST_URL) { json in
self.editView.text = json
}
}
만약에 4번 연속 비동기적인 작업이 필요하다면 아래와 같이 코드가 복잡해진다.
더불어서 에러처리, 특정 동작을 넣게된다면 이보다 훨씬 복잡해질 것이다.
func onLoad() {
editView.text = ""
downloadJson(MEMBER_LIST_URL) { json in
self.editView.text = json
downloadJson(MEMBER_LIST_URL) { json in
self.editView.text = json
downloadJson(MEMBER_LIST_URL) { json in
self.editView.text = json
downloadJson(MEMBER_LIST_URL) { json in
self.editView.text = json
}
}
}
}
}
개발자들은 이 과정이 복잡하고 귀찮다고 생각하게 된다.
아래 코드처럼 나중에생기는데이터<String?> 라는 반환값을 갖도록하고 completionHandler 대신에 사용할 순 없을까? 만약 가능하다면 보는 것과 같이 매우 간결한 코드 작성이 가능할 것이다.
func downloadJson(_ url: String) -> 나중에생기는데이터<String?> {
DispatchQueue.global().async {
// some code
}
}
func onLoad() {
editView.text = ""
let json: 나중에생기는데이터<String?> = downloadJson(MEMBER_LIST_URL)
json.나중에_데이터가_생기면_실행_메서드 { json in
self.editView.text = json
}
}
아래 코드처럼 class 나중에생기는데이터 를 구현하는 것으로 비동기 동작으로 인해 나중에 생기는 데이터를 반환하여 사용할 수 있는 방법 구현이 가능하다.
class 나중에생기는데이터<T> {
private let task: (@escaping (T) -> Void) -> Void
init(task: @escaping (T) -> Void) -> Void) {
self.task = task
}
func 나중에_데이터가_생기면_실행_메서드(_ f: @escaping (T) -> Void) {
task(f)
}
}
class ViewController {
func downloadJson(_ url: String) -> 나중에생기는데이터<String?> {
return 나중에생기는데이터() { f in
DispatchQueue.global().async {
// json을 받아오는 some code
f(json)
}
}
}
func onLoad() {
editView.text = ""
let json: 나중에생기는데이터<String?> = downloadJson(MEMBER_LIST_URL)
json.나중에_데이터가_생기면_실행_메서드 { json in
self.editView.text = json
}
}
}
위와 같은 과정을 거쳐 좀더 쉽고 간결하게 비동기 로직을 구현하기 위한 고민을 했고, 그렇게 탄생한 유틸리티 중 하나가 RxSwift 이다.
위에서 언급한 코드를 RxSwift로 바꾼다면 아래와 같아진다.
class ViewController {
// 나중에생기는데이터<String?> 타입은 Rx에 구현된 Observable<String?> 타입
func downloadJson(_ url: String) -> Observable<String?> {
return Observable.create() { f in
DispatchQueue.global().async {
// json을 받아오는 some code
f.onNext(json)
}
return Disposables.create()
}
}
func onLoad() {
editView.text = ""
// 나중에_데이터가_생기면_실행_메서드 -> subscribe
// subscribe는 evrnt를 받고 각 event별로 로직을 구현하면 된다.
downloadJson(MEMBER_LIST_URL)
.subscribe { event in
switch event {
case let .next(json):
self.editView.text = json
case .completed:
case .error:
break
}
}
}
}
이처럼 비동기 로직의 completionHandler 대신에 반환값을 사용하고,
Observable create() / Disposables.create() / subscribe 등 좀더 다양한 기능을 더한 것이 RxSwift이다.
RxSwift를 이해하는 것에 아래 두가지 방법이 큰 도움이 된다.
- 비동기로 생기는 데이터를 Observable로 감싸서 반환하는 방법
- Observable로 오는 데이터를 받아서 처리하는 방법