RxSwift?
지금까지 벨로그에서 RxSwift 에 대해 여러 포스트를 남겼다.
유튜브 "곰튀김" 님의 강의를 듣고 입문을 하게 된 것인데, 강의를 들으면서 이 기술을 어디에 써먹을 수 있을까... 를 계속 생각하면서 들었다.
공부를 하면서 느낀 점이 있는데,
나는 지금껏 앱 개발을 공부하면서 알게 모르게 비동기적인 반응형 프로그래밍을 구현하고 있었다.
이렇게 할 수 있었던 이유는, 비동기적 프로그래밍을 구현해주는 라이브러리가 많이 나와있어서이다. 예를 들어,
KingFisher 이라는 이미지 비동기 처리 라이브러리이다.
어느정도 캐시 처리도 해주고 있어서 내가 아주 애용하는 라이브러리인데,
RxSwift를 공부하고 이 코드를 보면 참 닮았다는 생각이 많이 든다.
또, Firebase의 코드를 살펴보면,
이렇게 Firebase 역시 Observe라는 단어를 쓰는 것이 참 닮았다는 생각이 든다. Firebase의 Observe는 RxSwift의 subscribe와 비슷한 기능을 한다.
RxSwift를 공부해야겠다고 생각이 들었던 이유는 Firebase에서 생겼다.
위에 코드를 보면 알겠지만
Firebase 의 RealtimeDatabase 는 위 사진처럼 데이터 트리 형태를 띈다. 그렇다면 깊숙하게 있는 데이터를 꺼낼 때는 코드가 더 길어진다는 뜻이 된다. 그렇기에 나는 코드를 줄이기 위해 적어도 사용자의 정보 정도는 간단하게
func getUserInfo(_ uid: String)->UserModel {
유저의 정보를 불러오는 코드..
}
정도로 만들어서 불러오고 싶었다.
하지만 불러오는 것은 가능하고, 리턴하는 것이 불가능 하다는 것을 깨닳았다.
왜냐면 위처럼 코드를 짠다고 가정했을 때,
func getUserInfo(_ uid: String) -> UserModel {
Database.database().reference().child("Users").child(uid!).observe(DataEventType.value) {
DataSnapShot in
let user = UserModel(Json: DataSnapShot.value as? [String: Any])
유저를 어디에서 리턴할 것인가??!!
여기서 리턴한다면, 에러가 나온다.
return user 불가능
}
그렇다고 여기서 리턴한다면,
데이터가 user 에 담기기 까지 시간이 걸리기 때문에,
담기기 전에 리턴이 되어버린다.
user = nil...
}
위처럼 된다.
인터넷 통신을 통한 데이터는 확실히 느리다.
그렇다면 그 "나중에 생기는 데이터" 를 객체로 만들어보자.
@escaping 으로 구현해보자.
class 나중에생기는데이터<T> {
private let task: (@escaping (T) -> Void) -> Void
init(task: @escaping (@escaping (T) -> Void) -> Void) {
self.task = task
}
func 데이터가받아지면(_ f: @escaping (T) -> Void) {
task(f)
}
}
이렇게 구현을 해볼 수 있겠다.
이러한 클래스를 RxSwift에서 Observable 이라고 부른다.
Observable 은 직역해보면 "관찰가능한" 이 된다. 아직 관찰하지 않았다.
.subscribe를 통해 "관찰"이 가능하다.
Observable 은 한번만 subscribe 가능하다.
Observable 이 subscribe 되면 Disposable 이 된다.
Disposable 은 직역하면 " 사용 후 버릴 수 있는" 이 된다.
아직 버리지 않았다.
.dispose() 를 통해 버릴 수 있다.
예문을 통해 알아보기
간단하게 url 에서 json 을 받아오는 코드를 짜보자.
MARK: 함수에서 async를 사용하는 방법 1
func downloadJson(_ url: String, _ completion: @escaping (String?) -> Void) {
DispatchQueue.global().async {
let url = URL(string: url)!
let data = try! Data(contentsOf: url)
let json = String(data: data, encoding: .utf8)
DispatchQueue.main.async {
completion(json)
}
}
}
비동기로 구현해야 하기 때문에 DispatchQueue.global().async 에서 데이터를 받아온 다음
downloadJson(URL_NAME) { json in
UI 처리.
}
UI처리는 DispatchQueue.main 에서 처리 해주면 끝.
이제 이 코드를 RxSwift로 구현해보자.
//MARK: 1. 비동기로 생기는 데이터를 Observable로 감싸서 리턴하는 방법
func downloadJson(_ url: String) -> Observable<String?> {
return Observable.create { emitter in
let url = URL(string: url)!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
guard error == nil else {
emitter.onError(error!)
return
}
if let dat = data, let json = String(data: dat, encoding: .utf8) {
emitter.onNext(json)
}
emitter.onCompleted()
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
나중에 생기는 데이터 , Observable 을 리턴해보자.
Observe.create 를 통해 Observable 을 생성하고
UrlSession에서 json을 받아온다.
에러가 발생하면 빠져나와 Subscribe 가 끝난 Observable , Disposable을 리턴한다.
위 함수를 만들어놓고 나면,
let observable = downloadJson(MEMBER_LIST_URL)
_ = observable.subscribe { event in
switch event {
case .next:
메인 쓰레드에서 ui처리
case .error:
break
case .completed:
break
}
}
앞으로는 어떠한 url 도 downloadJson으로 손쉽게 데이터를 불러올 수 있다.