RxSwift 맛만 보자 !!!

2020. 5. 15. 15:13iOS/RxSwift

오늘 볼 내용은 RxSwift입니다.

Swift를 공부하다보면 자주 들어볼 수 있는 있는데요. 또한 취업 공고를 보다보면 우대사행에 이 RxSwift를 볼 수가 있습니다.

그래서 Rx에 대해 한번 알아보겠습니다.

먼저 이 포스팅은 유튜브 곰튀김님의 자료 'RxSwift, 4시간 안에 빠르게 익혀 실무에 사용하기'안에 있는 내용입니다.

github.com/iamchiwon/RxSwift_In_4_Hours

www.youtube.com/watch?v=iHKBNYMWd5I&list=PL03rJBlpwTaBrhux_C8RmtWDI_kZSLvdQ

예제에서 주어진 Sample 코드에서는 다음과 같은 코드가 있습니다.

    @IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)

        let url = URL(string: MEMBER_LIST_URL)!
        let data = try! Data(contentsOf: url)
        let json = String(data: data, encoding: .utf8)
        self.editView.text = json

        self.setVisibleWithAnimation(self.activityIndicator, false)
    }

Load 란 버튼이 있습니다. 이 버튼을 누르면 URL을 통해 데이터를 가져와서 텍스트 뷰안에 집어넣는 작업을 합니다.

근데 만약 데이터의 양이 크다면 ? 데이터를 받아오는데 어느정도의 Term이 생기고 이때 동안 모든 동작이 멈추게 됩니다.

이 해결방법은 그렇다면 무엇이냐 ! -> 비동기 작업을 통해 다른 스레드로 이러한 데이터를 받아오는 작업을 해주는 것입니다 !

다음과 같이 말이죠.!

    @IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)

        // 비동기 작업
        DispatchQueue.global().async {
            let url = URL(string: MEMBER_LIST_URL)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
            }
        }
    }

DispatchQueue를 통해 데이터를 받아오는 작업을 비동기로 실행합니다. 여기서 잠깐 짚고 넘어가야 할 점은 바로 main 쓰레드의 호출인데 화면의 UI를 변경하는 작업은 main 쓰레드에서만 가능하기 때문에 따로 main을 호출하여 변경해야만 합니다.

이렇게 하면 멈춤 현상 없이 잘 돌아가는 것을 확인 해 볼 수 있습니다.

근데 이제 이러한 비동기 작업을 함수를 통해 한다면 좋아지겠죠 ...? 그래서 한번 ! 나누어보겠습니다.

    func downloadJson(_ url: String, _ completion: @escaping (String?) -> Void) {
        DispatchQueue.global().async {
            let url = URL(string: MEMBER_LIST_URL)!
            let data = try! Data(contentsOf: url)
            let json = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                completion(json)
            }
        }
    }
    @IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)

        downloadJson(MEMBER_LIST_URL) { json in
            self.editView.text = json
            self.setVisibleWithAnimation(self.activityIndicator, false)
        }
    }

비동기 작업을 위해 함수를 'downloadJson' 이라는 함수를 만들었습니다. 이 함수에서는 json데이터를 return 하여 전달해 줄 순 없습니다. 왜냐! 비동기를 통해 데이터를 받아와야하는데 만약 다 안받아와졌는데 메인스레드에서 함수가 종료가 된다면 ..! 안되겠죠 ?!

그래서 좀더 복잡한 completion으로 데이터를 전달해 주게 됨으로서 함수를 호출할 때 completion을 통해 데이터에 대한 처리작업을 해줄 수가 있습니다.

이제 이런 비동기 처리의 번거로움을 해결하기위해 이러한 나중에 생기는 데이터를 포장해주는 타입을 정의하여서, 함수에서는 이러한 데이터 타입을 바로 리턴할 수 있도록 하게 해줍니다..

class 나중에생기는데이터<T> {
    private var task: (@escaping (T) -> Void) -> Void

    init(task: @escaping (@escaping (T) -> Void) -> Void) {
        self.task = task
    }

    func 나중에오면(_ f: @escaping (T) -> Void) {
        task(f)
    }
}

조금 복잡해 보이지만 이러한 유틸리티를 만들어서 직접 적용시켜 보겠습니다.

    func downloadJson(_ url: String) -> 나중에생기는데이터<String?> {

        return 나중에생기는데이터() { f in
            DispatchQueue.global().async {
                let url = URL(string: MEMBER_LIST_URL)!
                let data = try! Data(contentsOf: url)
                let json = String(data: data, encoding: .utf8)
                DispatchQueue.main.async {
                    f(json)
                }
            }
        }
    }

    @IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)


        downloadJson(MEMBER_LIST_URL)
            .나중에오면 { json in
                self.editView.text = json
                self.setVisibleWithAnimation(self.activityIndicator, false)
        }
    }

이렇게 비동기 처리를 위한 유틸리티가 3가지가 있는데 Promisekit, Bolt 그리고 RxSwift 입니다.

Rx에서는 '나중에생기는데이터' -> Observable, 나중에오면 -> subscribe 라고 바꾸어서 사용해 볼 수 있습니다.

    func downloadJson(_ url: String) -> Observable<String?> {

        return Observable.create { f in
            DispatchQueue.global().async {
                let url = URL(string: MEMBER_LIST_URL)!
                let data = try! Data(contentsOf: url)
                let json = String(data: data, encoding: .utf8)
                DispatchQueue.main.async {
                    f.onNext(json)
                }
            }

            return Disposables.create()
        }
    }

    @IBAction func onLoad() {
        editView.text = ""
        setVisibleWithAnimation(activityIndicator, true)

        downloadJson(MEMBER_LIST_URL)
            .subscribe { event in
                switch event {
                case .next(let json):
                    self.editView.text = json
                    self.setVisibleWithAnimation(self.activityIndicator, false)
                case .error(_):
                    break
                case .completed:
                    break
                }
        }
    }

이렇게 RxSwift의 용도는 비동기적으로 생기는 데이터를 Completion이 아닌 return값으로 전달하기 위해서 만들어진 유틸리티 입니다.

오늘은 이렇게 RxSwift에 대해 정말 맛만 보았습니다.

Rx가 듣기는 많이 들었지만 높은 러닝커브로 인해 사실 접하기가 두려웠었는데 Rx가 왜쓰이는지에 대해 알아볼수 있었던 시간이였습니다