An API for as asynchronous programming with observable streams.
즉 observable streams을 이용하여 비동기 프로그래밍을 하기 위한 API이다. 이게 무슨 말인가 싶을 수 있는데 하나씩 살펴보도록 하자.
모바일 개발은 하면서 비동기
방식은 정말 필수적이다. 만약 핸드폰에서 어떠한 데이터를 불러올때마다 화면이 멈춰버린다면 정말 최악일 것이다.
보통 서버에서 이미지, 동영상 등 큰 파일을 가져올 때 main thread에서 동기적으로 수행시키려할 때 이러한 문제가 발생한다.
이를 극복하기 위해 보통 Grand Central Dispatch (GCD)를 통해 멀티스레드를 이용하여 비동기 기법을 사용하곤 한다.
이미 비동기 프로그래밍을 하게 해주는 것들이 있음에도 불구하고 RxSwift를 쓰는 이유는 쉬운 사용성 때문일 것이다.
Observable한 객체의 형태로 비동기적 작업과 데이터 스트림을 쉽게 구성할 수 있고 복잡한 Call-back에서 벗어나도록 한다.
또한 Operator들을 이용하여 여러 연산들을 쉽게 처리하기도 한다.
우선 ReactiveX 공식 홈페이지를 가면 크게
Observable, Operators, Single, Subject, Scheduler
총 5개로 나누어 소개한다.
이 중 오늘은 Observable, Operators에 대해 알아보자.
RxSwift의 가장 기본이 되는 것이 Observable이다.
Observable에서 들어오는 데이터를 어떠한 메커니즘에 따라 줄세워서 순서대로 처리하는데 이러한 것을 stream이라고 한다.
처음 공부할 때 Observer와 Observerable이 헷갈렸는데 쉽게 이렇게 생각하면 될 것 같다.
Observable
입장 : 해당 Event를 방출(emit)하여 Observer에게 전달하고 Observer에서 처리
Observer
입장 : Observerable을 구독(subscribe)하고 데이터를 받아 처리
물론 Observable은 관찰 가능한 상태이기 떄문에 비동기 처리 방식이 가능한 것이다.
subscribe는 operator들을 거치고 stream이 다 흐른 뒤 원했던 최종 값을 사용할 때 이용한다고 생각하고 handling 할 수 있는 방법은 크게 next, error complete 3가지 이다.
// MARK: - Observable 생성 1
let ob_just = Observable<Int>.just(1) //하나의 요소를 포함하는 Sequence 생성
let ob_of1 = Observable.of(1,2,3) // type을 추론해 Sequence 생성
let ob_of2 = Observable.of([1,2,3]) // 단일요소인 Array 이용 가능
let ob_from = Observable.from([1,2,3]) // .from은 오직 Array 타입만 처리해서 사용
let ob_range = Observable<Int>.range(start: 1, count: 10) //순차적인 요소를 갖는 Sequence 생성
// MARK: - Observable 생성 2
Observable<String>.create { o in
o.onNext("create")
o.onCompleted()
o.onError(MyError.myerror)
return Disposables.create() // 커스텀하게 생성 가능
}.subscribe(//addObserver와 비슷
onNext: {n in print(n)},
onError: {e in print(e)},
onCompleted: {print("completed")},
onDisposed: {print("disposed")}
).disposed(by: disposeBag)
}
//onComplete, onError, dispose 부분 제거하면 memory leak
여기서 Disposable은 Observable의 명시적인 중단을 위해 사용되며 생성한 subscription들을 하나의 disposeBang에 넣어 효율적으로 사용해주면 좋을 거같다. memory leak 발생 문제를 해결해주기도 하는데 어차피 깜빡하고 안써도 경고를 주니 참고하고 있자!
쉽게 Observable을 생성하고 변형하고 합치는 등 다양하게 연산을 할 수 있도록 도와주는 역할을 하며 이것이 RxSwift를 강력하게 만들어준다고 생각한다.
굉장히 많은 operators들이 있는데 경우에 따라 어떤 것을 사용할지는 문서에서 잘 설명해주기 때문에 외우지는 않고 그때 필요한대로 찾아 사용하자.
-> Operators 종류
이제 몇 가지 Operators에 대해 알아볼텐데 혹시 Operators들의 흐름에 대해 알고 싶으면 이곳을 참고하면 좋을거같다.
-> Operators 다이어그램
func exJust1() {
Observable.just("Hello World")
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)//complete-> disposableBag에서 사라짐
}
// --- 출력 ---
Hello World
//배열을 넣는다면 배열 자체가 그대로 한번에 출력
func exFrom1() {
Observable.from(["RxSwift", "In", "4", "Hours"])
subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
}
// --- 출력 ---
RxSwift
In
4
Hours
func exMap1() {
print("\nexMap1()")
Observable.just("Hello")
.map { str in "\(str) RxSwift" }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
}
// --- 출력 ---
Hello RxSwift
func exFilter() {
print("\nexFilter()")
Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.filter { $0 % 2 == 0 }
.subscribe(onNext: { n in
print(n)
})
.disposed(by: disposeBag)
}
// --- 출력 ---
2
4
6
8
10
let disposeBag = DisposeBag()
Observable.range(start: 1, count: 3).subscribe {
print($0)
}.disposed(by: disposeBag)
// --- 출력 ---
// next(1)
// next(2)
// next(3)
// complete
let disposeBag = DisposeBag()
let sequence1 = Observable<Int>.of(1,2)
let sequence2 = Observable<Int>.of(1,2)
let usingFlatMap = Observable.of(sequence1,sequence2)
usingFlatMap.flatMap{
return $0
}.subscribe(onNext:{
print($0)
}).disposed(by: disposeBag)
// --- 출력 ---
// 1
// 2
// 1
// 2
마지막으로 지금까지 배운 것을 바탕으로 위 코드를 보자면
우선 어떠한 Observable의 streams(흐름)을 나타낸 것이다.
이 흐름 속에서 데이터는 가공되고 필러링 되고 있는데 주목할 점은
operator들이 main thread에서 동기적으로 작동하는 것을 비동기적으로 바꿔주기 위해 비교적 시간이 소요되는 .map{UIImage(data :$0)}
앞에 다른 스케줄러로 지정해주는 것이다.
-> .subscribeOn(~)
스케줄러의 최소 단위는 thread이기 때문에 multi-thread로 비동기적인 실행이 가능해지는 것이다.