일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 스위프트
- Xcode
- uiscrollview
- clean architecture
- 클린 코드
- 리펙터링
- MVVM
- Clean Code
- tableView
- combine
- SWIFT
- swift documentation
- rxswift
- uitableview
- Protocol
- ribs
- collectionview
- swiftUI
- UITextView
- Refactoring
- HIG
- UICollectionView
- 리펙토링
- 애니메이션
- Human interface guide
- RxCocoa
- 리팩토링
- ios
- Observable
- map
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[RxSwift] 핵심 개념 본문
1. Observable
1) 용어
- Observable : 관측(subscribe)되는 대상
- Observer (=subscriber) : Observable을 구독(subscribe)하는 주체
- emit : Observable이 이벤트를 방출하는 것
2) Observable에 이벤트 생성
- .just(value)
- .of(value1, value2, ...)
- .from([Element]) : 오직 array타입만 가능, 원소 모두 하나씩 emit
- .create() : 클로저 형식, 다양한 값 생성 가능
Observable<Int>.create { observer in
observer.onNext(1)
observer.dispose()
}
3) Subscribe method
Observer가 Observable에 연결하는 것(Observer입장에서 Observable의 emit을 발생하게끔 하는 것)
(딱 3가지)
- onNext : Observable의 최신 값을 emit
- onError : 이벤트 발생 종료 (더 이상 onNext, onCompleted부르지 않음)
- onCompleted : 정상 종료
4) Observable의 Traits
- Single : .success, error (파일 쓰기, 파일 다운로드, 데이터 로딩)
- Maybe : .success, completed, error
- Completable : .completed, error (일의 끝, 오류 판단)
* success와 completed차이 : 파라미터 유무
.success(value)
.completed
* disposeBag : 이 변수가 dealloc되는 여기에 넣었던 Observable역시도 같이 해제
2. Operator
1) 용어
operators : emit된 이벤트들에 대해서 변형&처리&반응 할 수 있는 방법
Chaining Operators : 이전의 연산자와 함께 "체인"형식으로 접근이 간능(독립적이지 않고 모두 종속적으로 발생)
2) Transforming operator
- toArray : 개별 원소 -> 하나의 배열
- map : 배열 순회하면서 1대1매핑
- flatMap
3) Combine operator
- .concat([Observable])
let left = Observable.of(9, 8, 7)
let right = Observable.of(1, 2, 3)
let concatTest = Observable.concat([left, right])
concatTest
.subscribe(
onNext: {print($0)}
)
// 9 8 7 1 2 3
- .merge: 무조건 선착순
let source = Observable.of(left, right)
left observable = source.merge()
observable.subscribe(
onNext:{print($0)}
)
// 9 8 1 7 2 3
- combineLatest : 여러개의 seqeunce들의 event들을 하나의 sequence에 동시에 저장
Observable.combineLatest(v1,v2)
- zip : combineLatest와 유사하지만 짝을 기다림
4) Trigger operator : 특정 시점에 emit하게끔 설정
- 옵저버1.withLatestFrom(옵저버2) : "옵저버1"이 onNext될 때, "옵저버2"의 최신 event와 같이 emit함
- 옵저버1.sample(옵저버2) : "옵저버2"가 onNext될 때, "옵저버1"이 emit함, 단 하나의 값만 발동
5) left.amb(right) : left와 right중 먼저 발생한 event에 속하는 subscriber만 수신
6) seqenece 내 요소들간의 결합
- reduce(초기값, accumulator클로저) : 하나의 값으로 압축
- scan(초기값, accumulator클로저) : reduce와 동일하지만 누적하며 emit
7) shared연산자
자원을 공유해서 효율적으로 observable을 여럿이서 subscribe할 때 사용
3. Subject
1) 개념 : Observable과 Observer간의 연결(특별한 제약사항을 걸어둔 Observable)
2) 종류
- PublishSubject : empty시작 / 최신 요소만 emit
- BehaviorSubject : 초기값으로 시작 ,,, BehaviorSubject(value: "init val")
- ReplaySubject : 초기화된 버퍼 사이즈로 시작(정한 갯수만큼 다수 emit) ,,, ReplaySubject<String>.create(bufferSize: 2)
- Relay : 절대 끝나지 않음, 오직 .next이벤트만 가능 (PublishRelay, BehaviorRelay)
- combineLatest : 여러개의 이벤트를 저장 ,,, Oservable.combineLatest(left, right)
ex)
Images = BehaviorRelay<[UIImage]>(value: [])
let newImages = images.value
+ [UIImage(named: "IMG_1907.jpg")!]
images.accept(newImages) // accept()의미 : 이벤트 수락(등록)
3) subscribe 방법 : capture list이용
images.subscribe(
onNext: { [weak imagePreview] photos in
guard let preview = imagePreview else { return }
preview.image = photos.collage(size: preview.frame.size) }
)
.disposed(by: bag)
4. Scheduler
Scheduler : 스케줄러는 쓰레드가 아님
- observeOn(scheduler) : Observable이 Observer에게 알리는 스케줄러를 지정
- SubscribeOn(scheduler) : Observable이 동작하는 스케줄러 지정
(쉽게 다시 정리)
RxSwift
핵심은 비동기 이벤트들을 stream에 담아서 코드로 처리하겠다는 목표
Scheduler
- 동시에 실행 : ConcurrentDispatchQueueScheduler(qos: .default)
- 메인실행 : MainSCheduler.instance
- observeOn(MainScheduler.instance) : 선언한 밑 부분부터 적용
- subscribeOn(ConcurrentDispatchQueueScheduler(sos: .default)) : subscribe하는 시점부터 실행 (아무곳에 선언해도 가능)
비동기를 잡아서 밑 순서대로 진행한다고 생각,
map은 해당 원소에 접근하는 것이라 생각
Observable.Just|(“800x600”)
.map{$0.replacingOccurrences(9f: “x”, with: “/“)} // 800/600
.subscribeOn(ConcurrentDispatchWueueScheduler(sos: .default))
.observeOn(MainScheduler.instance)
.subscribe(onNext: self.imageView.image = $0 }
Side effect
- self(외부 블럭에 영향을 미칠 수 있는 곳) —> 딱 두 곳에만 가능하게끔 약속
- Do
- subscribe
(지나가면서 do를 실행)
—————————————————————subject———————————————————
subject란?
subscribe도 가능하고 이벤트도 추가해줄 수 있는 애
BehaviorSubject
PublishSubject
ReplaySubject : 버퍼 사이즈만큼 전달
————————— RxCocoa ————————————————————————————————
textField.rx —> 얘네들을 비동기로 받을 것이다. 선언. 단, 모든 값을 Optional로 처음에 받음
- 다음과 같이 filter와 map에서 unwrap해줄 수 있지만, “orEmpty”로 옵셔널 바인딩 가능
- 주의할 점 : map에서 다른 메소드를 호출 할 경우, 메소드 이름만 적음
Optional binding
주의 -> map해서 나오는 값은 checkEmailValid함수에서 반환된 값 (boolean)
textField.rx.text
.subscribe(onNext {print($0)}
.disposed(by: db)
textField.rx —> Reactive
textField.rx.text 타입 —> ControlProperty … rx로 컴포넌트에 바인딩을 할 때 사용되어짐
textField.rx.text.asObservable() —> Observable<String?>
- 개념 … 개념 더 찾아보기 —> 이 개념을 알아야 Observable이라는 개념을 이해할 수 있음
- Reactive : Use Reactive proxy as customization point for constrained protocol extensions.
- ControlProperty : Trait for Observable/ObservableType that represents property of UI element.
- Binder : Observer that enforces interface binding rules:
.combineLatest(source1, source2, resultSelector: ) : 두 값을 받아서 결정하여 하나를 return
- 두 텍스트필드의 유효에 대한 것을 검정 한 후 ,
.zip : 두 개를 주면, 둘 다 데이터가 만들어진 경우 전달 (한쪽의 데이터가 바뀌고 다른쪽은 안바뀐 경우 아직 전달 x)
combineLatest : 두 개의 스트림에서, 한쪽의 데이터만 들어와도 최신의 값들을 두 스트림에서 비교함
Merge : 두 개의 스트림을 받아서, 들어오는 대로 하나씩 받음 (한 스트림에서 두 개 비교하게 될 수 있음)
Bind : Subject를 통해 input과 output으로 나눔
idValid: BehaviorSubject<Bool> = BehaviorSubject(value: false)
— input
idValidOb = idField.rx.text.orEmpty
idValidOb.map(checkPasswordValid).subscribe(onNext: {self.idValid.onNext($0)})
idValidOb.map(checkPasswordValid).bind(to: idValid) // subscroibe의 onNext와 같은 것
// bind의 리턴값은 Disposable이므로, .dispose해줄 것
— output
idValid.subscribe(onNext: {b in
self.idValidView.isHidden = b
})
.disposed(by: disposeBag)
drive()
: UI에서 처리할 때 mainThread로 돌려줘야 하는데, 아래와같이 쓰는게 귀찮아서 씀
.observeOn(MainScheduler.instance) —> 쓰는게 귀찮아서 씀
.asDriver()
.drive(onNext: {idValidView.isHidden $0})
.bind(to: idValidView.rx.isHidden)
Memory leak 방지
안쓰면 ViewController가 pop되어도, self에 대한 Reference count가 남아있기 때문에 없어지지 않는 위험 방지 —> 캡쳐리스트로 count 증가시켜주지 않게함
—> UI에 관한 것은 complete되지 않기 때문에 캡쳐리스트를 사용해야함 (disposeBag이 사라질때 초기화됨)
—> complete되면 삭제됨
(complete가 명확한 애들 : .next로 추가한 이벤트들 …)