1교시, 2교시는 곰튀김님이 분류하신것이 아닌 제가 임의로 끊어들은 것을 기준으로 함을 알립니다,,🚨
( 깃헙 메인에 있는 리드미는 시즌1이 아니라 시즌2 목차였던 것.. 어째 순서가 안맞는다 했음ㅠ-ㅠ )
Scheduler
RxSwift의 중요 개념인 5가지(Observable, Operator, Scheduler, Subject, Single)중에 3번째인 Scheduler!
설명이 어려운데 간단히 설명하면 스케쥴러 = 스레드이다.
메인스레드 이외에 네트워크 작업 등을 백그라운드에서 비동기적으로 실행하고 싶으면 멀티 스레딩을 통해 스레드 분기 처리를 해주는 것.
스케쥴러를 사용하기 위한 연산자에는 두 가지가 있다.
- observeOn
: 파라미터로 스케쥴러를 받으면 그 다음줄부터 끝까지 / 혹은 새로운 observeOn 연산자를 사용하기 전까지 해당 스케쥴러에서 코드가 실행된다.
@IBAction func exMap3() {
Observable.just("800x600")
.map { $0.replacingOccurrences(of: "x", with: "/") } // "800/600"
.map { "https://picsum.photos/\($0)/?random" }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
.map { try Data(contentsOf: $0) }
.map { UIImage(data: $0) }
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)
}
위 코드는 이미지 데이터를 가져와서 이미지뷰에서 뿌려주는 데이터이다.
그런데 이미지 데이터를 받아오는 부분이 시간이 오래걸리고, 현재 하나의 메인 스레드에서 실행되고 있기 때문에
해당 코드가 실행되는 동안 메인 스레드를 실행하는 다른 코드들은 멈춰버리는 상황이다.
observeOn의 파라미터에 스케쥴러를 넣어주고
- 하나는 데이터 받아오는 부분 이전에,
- 나머지 하나는 는 이미지뷰에 뿌려주기 이전에 메인스케쥴러를 파라미터로 한 observeOn 연산자를 사용해준다.
그러면 데이터를 받아오는 ConcurrentDispatchQueueScheduler와, UI작업을 처리하는 MainScheduler 두 가지지 스케쥴러가 돌아가는 상황.
데이터 작업이 메인스케쥴러로부터 분리되었기 때문에 메인스케쥴러를 사용한 다른 작업들을 병렬적으로 실행할 수 있게된다.
- SubscribeOn
: 코드에서 .subscribe 연산자가 호출된 시점 이후에 동작할 스케쥴러를 지정해준다.
다시 상기해보면 Observable은 subscribe되기 전까지는 아무런 이벤트를 방출하지 않는다.
그러므로 subscribeOn 연산자는 subscribe된 이후[observable 생성] observable이 시작될 스케쥴러를 지정해주는 것.
=> 따라서 observeOn 연산자와 다르게, subscribe 연산자 사용 이전이라면 코드 어느 곳에 써놓아도 무방하다.
말이 좀 애매해서 공식사이트를 찾아보았다.
그러니까 observeOn 연산자는 '연산자(filter, map..)가 실행될 스케쥴러를 지정'해주고
subscribeOn 연산자는 'Observable이 시작될 스케쥴러를 지정'해준다.
때문에 observeOn 연산자는 여러 번 호출해도 되지만, subscribeOn 연산자는 일반적으로 1번만 호출한다.
* Side Effect : 외부에 영향을 주는 부분
위 예제 코드에서 imageView에 이미지를 넣는 부분은 Side effect에 해당한다.
이런 Side effect는 일반적으로 subscribe 부분, 혹은 do 연산자 내부에 정의해준다.
- do 연산자 : stream을 지나는 동안에 side effect 이벤트가 들어오는 경우 사용.
이렇게 스트림을 지나다가 중간에 이벤트가 들어오면 해당 side effect를 처리해주는 것.
RxCocoa 기본
RxCocoa : UIKit의 RxSwift extension 라이브러리
화면의 오브젝트들에 비동기 처리를 해주기 위해서는 UIKit을 위한 별도의 extension이 필요하다.
그러니까 RxCocoa를 오브젝트들(view, textfield..)에 적용하기 위한 extension 라이브러리인 것.
예제 - 로그인 화면의 TextField 비동기 처리
1) 이메일 형식 확인
2) 비밀번호 형식 확인
3) 이메일, 비밀번호가 모두 맞으면 -> login 가능
1. 이메일, 비밀번호 형식 확인
private func bindUI() {
idField.rx.text
.subscribe(onNext: { s in
print(s)
})
.disposed(by: disposeBag)
}
idField.rx.text -> 부분이 RxCocoa를 사용한 부분.
id textfield에 글자를 입력할 때마다 내용이 print된다.
idField.rx.text
.filter { $0 != nil }
.map { $0! }
.subscribe(onNext: { s in
print(s)
})
.disposed(by: disposeBag)
해당 내용이 nil이 아닐 때만 print한다.
idField.rx.text.orEmpty
.map(checkEmailValid)
.subscribe(onNext: { b in
self.idValidView.isHidden = b
})
.disposed(by: disposeBag)
private func checkEmailValid(_ email: String) -> Bool {
return email.contains("@") && email.contains(".")
}
nil check는 orEmpty 연산자로 대체 가능.
checkEmailValid는 idField가 nil인지를 체크해서 bool값을 리턴해주는 함수이다.
그리고 그 bool값에 따라 isValieView(빨간색 동그라미)의 표시 여부를 결정한다.
같은 코드를 변형하면 비밀번호도 확인할 수 있다.
그러면 이제 비동기적으로 이메일, 비밀번호를 체크할 수 있게 되었다😃
2. LOG IN 버튼 활성화
이제는 email, pw 두 개를 확인해서 모두 형식이 일치하면 로그인 버튼이 활성화되도록 만들어야 한다.
Observable.combineLatest(
idField.rx.text.orEmpty.map(checkEmailValid),
pwField.rx.text.orEmpty.map(checkPasswordValid),
resultSelector: { s1, s2 in s1 && s2}
)
.subscribe(onNext: { b in
self.pwValidView.isHidden = b
})
.disposed(by: disposeBag)
combineLatest 연산자는 복수의 Observable을 받아서 함수를 적용 결과를 출력한다.
위 코드는 idField와 pwField에 관한 두 개의 Observable을 resultSelector 내부의 함수에 적용시킨다.
s1 && s2라고 되어있으니 idField, pwField의 check값이 모두 true인 경우만 아래로 내려보내는 것.
그러면 이제 onNext의 인자 b에는 true가 들어가게된다.
* combineLatest는 가장 최근에 변경된 스트림을 내려보내는데,
zip 연산자는 데이터가 '모두' 생성되면 전달해주고
merge는 select가 아닌 순서대로 하나씩 모두 내려보내는 연산자이다.
'RxSwift' 카테고리의 다른 글
[RxSwift] 알엑스로 마리모 다이어리 v1.1 리팩토링하기 - 1 (0) | 2021.12.20 |
---|---|
[RxSwift] 곰튀김님의 RxSwift 4시간 끝내기 정리 - 4교시 (0) | 2021.11.08 |
[RxSwift] 곰튀김님의 RxSwift 4시간 끝내기 정리 - 3교시 (0) | 2021.11.01 |
[RxSwift] 곰튀김님의 RxSwift 4시간 끝내기 정리 - 1교시 (0) | 2021.10.12 |
댓글