[iOS] - RxSwift 알아보기

Shawn·2021년 7월 13일
0

RxSwift

목록 보기
6/8
post-thumbnail

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으로 손쉽게 데이터를 불러올 수 있다.

profile
iOS 개발, Flutter 개발, Swift, Dart, 42 Seoul 3기

0개의 댓글