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의 예제로 안내된 코드이다. 마블 다이어그램을 보면 동그라미로 방출, 선으로 완료를 나타낸다.
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 |
---|
댓글