본문 바로가기
공부하기/RxSwift

RxSwift vs URLSession 이미지 다운로드 비교

by hyunjicraft 2021. 2. 28.

RxSwift와 조금 더 친해지기 위해 기존 코드를 RxSwift를 이용한 코드로 변경해보는 간단한 예제를 실습해보았다.

간단한 이미지 다운로더

 

1. Storyboard 에 다음과 같이 UIImageView와 버튼들을 Constraint와 함께 추가한다.

 

2.  Cocoapods 필수! Podfile에 RxSwift 라이브러리를 추가한 후 install 한다.

///Podfile

pod "RxSwift"

 

3. 메인 ViewController 상단에 import를 추가한다.

///ViewController.swift

import RxSwift

 

4. 상단에 IBOutlet을 연결하고 url 주소를 선언한다. 다운로드받을 이미지는 구글 검색에서 찾은 1920X1080 크기의 배경화면 이미지이다.

///ViewController.swift

@IBOutlet weak var imageView: UIImageView!
        
let url = URL(string: "https://wallpapercave.com/wp/wp7864479.png")

 

5. URLSession을 이용한 이미지 다운로드 함수를 구현한다.

///ViewController.swift

func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) -> URLSessionDataTask {
        
    let task = URLSession.shared.dataTask(with: url) { data, _, _ in
           
        guard let data = data else {
            completion(nil)
            return
        }

        let image = UIImage(data: data)
        completion(image)
    }
    task.resume()
    return task
}

 

6. 상단에  sessionTask를 중간에 취소할 수 있도록 property를 선언한다.

///ViewController.swift

var sessionTask : URLSessionDataTask?

 

7. Download 버튼에 IBAction을 연결하고 loadImage(url: , completion:) 함수를 호출한다. 이 때 loadImage 함수가 리턴하는 URLSessionDataTask를 sessionTask에 대입한다.

///ViewController.swift

@IBAction func download(_ sender: Any) {

    sessionTask = loadImage(url: url!, completion: { (image) in
        DispatchQueue.main.async { [weak self] in
            self?.imageView.image = image
        }
    })
}

 

8. Cancel 버튼에 IBAction을 연결하고 클릭 시 sessionTask를 cancel한다. 이미지 다운로드를 중간에 취소할 수 있다.

///ViewController.swift

@IBAction func cancel(_ sender: Any) {
 
    sessionTask?.cancel()
    imageView.image = nil
}

여기까지 완료하면 기본 Swift 함수로 구현이 끝났다. 
이제 RxSwift를 이용한 방법으로 변경해보자.


1. Observable을 리턴하는 loadImage 함수를 오버로드한다. 이 함수는 Observable을 리턴하는데 이 Observable은 UIImage를 emit(방출)할 수도 있고 아닐 수도 있다. Docs에 안내된 Sample code를 참고해서 Observable을 구현했다.

create 연산자는 Disposables.create() 으로 끝나고 Observable을 dispose할 때 필요한 처리를 구현한다. (dispose에 대해서는 나중에 다룰 예정이다.)

///ViewController.swift

func loadImage(url: URL) -> Observable<UIImage?> {
    return Observable<UIImage?>.create { emitter in

        let task = URLSession.shared.dataTask(with: url) { data, _, _ in

            guard let data = data else {
                emitter.onNext(nil)
                emitter.onCompleted()
                return
            }

            let image = UIImage(data: data)
            emitter.onNext(image)
            emitter.onCompleted()
        }
        task.resume()

        return Disposables.create {
            task.cancel()
        }
    }
}

 

아래는 Observable create의 예제로 안내된 코드이다. 마블 다이어그램을 보면 동그라미로 방출, 선으로 완료를 나타낸다.

 

출처 : http://reactivex.io

 

출처 : http://reactivex.io

 

 

2. sessionTask와 똑같이 cancel이 가능하도록 Observerble 변수를 저장할 property를 선언한다.

///ViewController.swift

var disposable : Disposable?

 

3. download 버튼의 IBAction 메소드를 변경한다.

연산자를 아직 정확하게 배우지 않았지만 미리 짧게 이야기하자면 observeOn 연산자는 특정 스케줄러 상에서 동작하길 바랄 때 상단에 사용하고, subscribe 연산자는 observable이 emit할 때 작용한다. onNext와 onCompleted는 마블 다이어그램의 시점과 같다. 

여기서는 onNext 시 image를 방출하기 때문에 imageView에 이미지를 로드할 수 있다.

참고로 구현한 onCompleted 외에 onError, onDisposed 등의 시점도 사용할 수 있다.

///ViewController.swift

@IBAction func download(_ sender: Any) {
//        sessionTask = loadImage(url: url!, completion: { (image) in
//            DispatchQueue.main.async { [weak self] in
//                self?.imageView.image = image
//            }
//        })
        
    disposable = loadImage(url: url!)
        .observe(on: MainScheduler.instance)
        .subscribe(onNext: { (image) in
            self.imageView.image = image
        }, onCompleted: {
            print("completed")
        })
}

 

4. 이제 마지막으로 cancel 버튼의 IBAction 메소드를 변경한다. dispose() 메소드는 URLSessionDataTask의 cancel과 비슷한 역할을 한다.

///ViewController.swift

@IBAction func cancel(_ sender: Any) {
//        sessionTask?.cancel()
    disposable?.dispose()

    imageView.image = nil
}

 


간단한 예제이기 때문에 모든 것을 다 적용할 수는 없지만 이 예제를 통해 알 수 있는 부분은 "Observable 이라는 연속된 항목(홈페이지에서는 flip이라고 표현된다.) 위에서 일어나는 모든 일들을 하나로 묶어서 구현할 수 있고 사람의 시점을 따르는 모양으로 구현되기 때문에 가독성 있는 코드가 완성된다."라는 것이다.

 

 

 

'공부하기 > RxSwift' 카테고리의 다른 글

ReactiveX 사이트 방문해보기  (0) 2021.02.24

댓글