일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- collectionview
- rxswift
- UITextView
- 리팩토링
- 리펙터링
- Refactoring
- tableView
- 리펙토링
- clean architecture
- uiscrollview
- HIG
- uitableview
- Protocol
- Clean Code
- 애니메이션
- swift documentation
- Observable
- Human interface guide
- UICollectionView
- ios
- RxCocoa
- map
- SWIFT
- Xcode
- combine
- 스위프트
- 클린 코드
- swiftUI
- MVVM
- ribs
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[RxSwift] 2. Observables 본문
1. Observables관련 용어 및 기능
* Observable이란 여러 이벤트들을 생성(="emit")할 수 있는 대상
* sequence 란?
* Observable == Observable sequence == sequence
"observable"을 "sequence"라고 부르는 이유?
1) next : 구성요소를 계속해서 방출시킬 수 있는 기능 (=observable구독자에게 데이터 전달)
2) completed : 이벤트를 종료시킬 수 있는 기능 (=observable구독자에게 완료되었음을 알림)
- 그림에서 끝에 막대기가 세로로 있는 것(이미 일이 끝난 상태)
3) error : 이벤트에 오류가 있음을 알고 중간에 종료시킬 수 있는 기능(observable구독자에게 오류를 알림)
- 이벤트에 오류를 포함하고 있는 경우 "X"로 표현
2. Observable(sequence) 생성
1) Observable.just() : 오직 하나의 요소를 포함하는 "Sequence"생성
- [one, two, three]처럼 하나의 배열로도 삽입 가능
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable = Observable<Int>.just(one)
}
(example메소드)
public func example(of description: String,
action: () -> Void) {
print("\n--- Example of:", description, "---")
action()
}
2) Observable.of() : 타입 추론을 이용하며 "Sequence"생성
- 3개의 integer값에 대한 sequence 생성
let observable2_1 = Observable.of(one, two, three)
* .of()로 단일요소 만들기 (.just()와 동일한 결과)
let observable3_1 = Observable.of([one, two, three])
let observable3_2 = Observable.just([one, two, three])
※ [one, tow, three]와 (one, two, three)의 차이를 주의(단일 요소인 배열, 배열이 아닌 다중 요소)
3) Observable.from() : 오직 array타입만 처리하며 각각 요소들을 하나씩 "emit"하는 기능
let observable4 = Observable.from([one, two, three])
4) Obervable.create()
클로저 형식이며 다양한 값(onNext, onCompleted, ...)을 생성할 수 있음
* disposeBag : 쓰레기봉투
example(of: "create") {
let disposeBag = DisposeBag()
let observable = Observable<String>.create({ (observer) -> Disposable in
observer.onNext("1")
observer.onCompleted()
observer.onNext("?")
return Disposables.create()
})
observable.subscribe{(event) in
print(event)
}
/* prints
next(1)
completed
*/
}
// 위 Disposeables와 subscribe 내용은 뒤에서 계속
5) Observable.empty()
6) Observable.never()
7) Observable.range(start:1, count: 10)
8) Observable.create()
* 위 내용은 뒤에서 설명
3. Observable subscribing
"옵저버에 대한 구독" 즉, 옵저버에 담긴 이벤트들을 방출(emit)하는 것이 subscribe메소드를 사용하는 것
1) observable.subscribe()
- 사전 작업 : Observable에 요소 추가
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
}
-구독하기 : subscribe
observable.subscribe { event in
print(event)
/*
--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed
*/
}
.subscribe는 정수의 이벤트 객체를 파라미터로 하는 escaping 클로저 형식 메소드, 반환값은 Disposable
2) observable.subscribe(onNext:)
next요소만 처리한다는 의미 (위에서 그냥 subscribe메소드는 completed까지 출력 했지만 이것은 아닌것을 확인)
observable.subscribe(onNext: { element in
print(element)
/* prints
1
2
3
*/
})
3) empty()로 설정된 Observable
subscribe()시, "completed"만 출력
example(of: "empty") {
let observable = Observable<Void>.empty()
observable.subscribe(
onNext: { (element) in
print(element)
},
onCompleted: {
print("Completed")
}
)
}
/* Prints:
Completed
*/
4) never()로 설정된 Observable
subscribe()시, "completed"도 출력되지 않음
example(of: "never") {
let observable = Observable<Any>.never()
observable
.subscribe(
onNext: { (element) in
print(element)
},
onCompleted: {
print("Completed")
}
)
}
5) range()로 설정된 Obervable
클로저에서 인수를 주목 "(i) in"
example(of: "range") {
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(onNext: { (i) in
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) / 2.23606).rounded())
print(fibonacci)
})
}
4. disposing
subscribe가 Observables안에 있는 이벤트들을 방출(emit)하는 것이라면,
disposing은 subscribe를 취소하는 것
1) dispose()
지금은 Observable의 이벤트가 3개있어서 3개 출력 후 "completed"를 출력하지만,
Observable의 이벤트가 무한대가 등록되어 있다면 일정 수준에서 dispose()에서 종료후 "completed"출력
example(of: "dispose") {
let observable = Observable.of("A", "B", "C")
let subscription = observable.subscribe({ (event) in
print(event)
})
subscription.dispose()
}
2) disposeBag
dispose에 대한 리턴값을 담는 객체
example(of: "DisposeBag") {
let disposeBag = DisposeBag()
Observable.of("A", "B", "C")
.subscribe{
print($0)
}
.disposed(by: disposeBag) // subscribe로부터 방출된 리턴 값을 disposeBag에 추가
print(disposeBag)
// prints : RxSwift.DisposeBag
}
3) Observable.create에서의 dispose
- completed는 작동하지 않음
enum MyError: Error {
case anError
}
example(of: "create") {
let disposeBag = DisposeBag()
Observable<String>.create({ (observer) -> Disposable in
observer.onNext("1")
observer.onError(MyError.anError)
observer.onCompleted()
observer.onNext("?")
return Disposables.create()
})
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
).disposed(by: disposeBag) // 이 구문이 있는 이유 : 메로리 제거함으로써 메모리 효율확보
}
/* Prints:
1
anError
Disposed
*/
* 코드의 흐름 : 동기가 아닌 비동기이므로 주의(crete블록과 subscribe블록 동시에 실행이라고 생각)
4) Obervable.deferred()
* deferred : 연기된
"Obervable Factory" 개념 : lazy var변수와 같이 Obervable.scribe()하는 순간 observable.deferred()가 실행
example(of: "deferred") {
let disposeBag = DisposeBag()
var flip = false
let factory: Observable<Int> = Observable.deferred(){
flip = !flip
if flip {
return Observable.of(1,2,3)
} else {
return Observable.of(4,5,6)
}
}
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
}
5. Traits
코드 가독상 상승, Observable 보다 좁은 범위를 선택할 수 있음
1) Single : 성공이냐, 실패냐를 따지는 one-time 일에 적합 (파일 다운로드, 디스크로딩)
- Single(.success(value)) = .next + .completed
- Single(.error(value))
2) Completable : single과 기능이 유사하지만, value값을 emit하지 않음
즉, 일이 제대로 됐는지만 검토할 때 사용(파일 쓰기)
- Completable(.completed)
- Completable(.error(value))
3) Maybe : Single + Completable
ex) Single
example(of: "Single") {
let disposeBag = DisposeBag()
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}
func loadText(from name: String) -> Single<String> {
return Single.create{ single in
let disposable = Disposables.create()
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}
guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}
guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}
single(.success(contents))
return disposable
}
}
// test
loadText(from: "myFile")
.subscribe{
switch $0 {
case .success(let string):
print(string)
case .error(let error):
print(error)
}
}
.disposed(by: disposeBag)
// prints: fileNotFound
}
6. Do와 debug
1) Observable.do
do연산자는 Observable이 never이든 뭐든 간에 일단 실행
- Observable.never() 연산자는 "completed"도 출력하지 않음
- 기존에 never를 하면 .subscribe()가 실행되지만 "completed"가 실행되지 않아서 subscribe()를 동작했는지 유무도 알 수 없었음
- do를 사용하면 subscribe()가 발생된지 알 수 있음
example(of: "never") {
let observable = Observable<Any>.never()
let disposeBag = DisposeBag()
// 구독했음을 알리는 print("Subscribed")
observable.do(
onSubscribe: { print("Subscribed")}
).subscribe(
onNext: { (element) in
print(element)
},
onCompleted: {
print("Completed")
}
)
.disposed(by: disposeBag)
}
// prints: Subscribed
2) Observable.debug()
- debug연산자로 출력문을 통해 디버그할 수 있음
콘솔창 출력 형식 : "날짜 시간: \(작성한문자열) -> <subscribed/isDisposed>"
example(of: "never") {
let observable = Observable<Any>.never()
let disposeBag = DisposeBag()
observable
.debug("never 확인")
.subscribe()
.disposed(by: disposeBag)
/* prints
2020-05-21 19:46:23.534: never 확인 -> subscribed
2020-05-21 19:46:23.536: never 확인 -> isDisposed
*/
}
*참조 : RxSwift: Reactive Programming with Swift, by raywenderlich team's book
'RxSwift > RxSwift 기본' 카테고리의 다른 글
[RxSwift] 6.Filtering Operators 실습 (0) | 2020.05.29 |
---|---|
[RxSwift] 5.Filtering Operators (0) | 2020.05.26 |
[RxSwift] 4. Observables and Subjects 실전 적용 (0) | 2020.05.25 |
[RxSwift] 3. Subjects (0) | 2020.05.22 |
[RxSwift] 1. RxSwift의 개념 (0) | 2020.05.20 |