티스토리 뷰
Collection타입들은 sequence 프로토콜을 채택하고 있음
-> 한 번에 하나씩 단계별로 진행할 수 있는 값 목록
-> 반복문 생각하면될듯
Observable과 Observer
옵저버블은 이벤트를 보내고, 옵저버는 이벤트를 처리한다고 생각
youtube를 예시로 들어보면, 영상을 아무리 많이 올려도 구독하는 사람이 없으면 볼 사람이 없음
처리하는 이벤트가 영상 시청이라고 생각하면 될듯
Infinite Observable Sequences, Finite Observable Sequences
말그대로 무한과 유한
무한은 UI같은거 생각하면될듯.
-> 데이터에 따라 UI변경이 일어나는건 계속해서 일어날 수 있음
유한은 파일 다운로드 생각하면됨.
-> 1기가 파일을 다운받을때, 한 번에 다운되는게 아니라 점진적으로 다운이 됨(10% - 20% - ...)
-> 그리고 다운이 끝나면 해당 다운 이벤트는 끝나는 거임
Observable
rx에선 세 가지 생각
next -> 이벤트를 emit. 예를 들어, 점진적인 다운로드 등
completed -> 성공. 다운 완료 => 이미지뷰 이미지 바꾸기 등의 것들을 하면 될듯
error -> 실패. 에러 => alert같은걸로 실패했다고 띄우기
complete와 error는 지속적으로 emit되지않고 한 번만
complete와 error는 항상 마지막에 호출
complete와 error는 동시에 호출될 수 없음
complete와 error가 실행될 수 있다면 유한한 시퀀스라고 생각해도됨.
시퀀스가 종료되는 시점에 dispose
옵저버와 옵저버블 연결하는 걸 subscribe라고 생각하면됨
-> subscribe가 없으면 이벤트가 아무리 emit되어도 처리할 수가 없음.
rx에는 다양한 오퍼레이터가 존재하는데 다 너무 많아서 자주 쓰는 것만 알면될듯
Disposable
subscribe중인 Stream을 원하는 시기에 처리할 수 있게 도와줌
모든 Observable은 Disposable을 return 하는데, Stream을 종료하고 실행되던 시퀀스를 동료
-> rx코드에서 마지막에 dispose있음
next이벤트가 무한히 emit된다면 dispose되지 않음
메모리 정리에도 도움이됨.
Rx 사용
TableView
simpleTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items
.bind(to: simpleTableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(row)"
return cell
}
.disposed(by: disposeBag)
//modelSelected는 didselected라고 생각
simpleTableView.rx.modelSelected(String.self) //이상태에서는 에러나 컴플리트 발생할 수 없음
//섭스크라이브는 성공한 케이스, onNext라는 매개변수가 생략된거, dispose는 메모리 삭제할때
// .subscribe { value in
// print(value)
// } onError: { error in
// print("error")
// } onCompleted: {
// print("completed")
// } onDisposed: {
// print("disposed")
// }
// .disposed(by: disposeBag)
.map { data in
"\(data)를 클릭"
}
.bind(to: simpleLabel.rx.text) //onNext이벤트밖에 없는거
.disposed(by: disposeBag)
내부에 다 구현이 되어있어서 따로 프로토콜을 채택해서 구현하지 않아도 됐음
just로 특정 값을 emit
보통 subscribe와 bind를 사용하는데, 무조건 성공하고 에러가 없는 경우(버튼 클릭 등)은 bind로 사용하는 듯
-> subScribe에서 onNext만 쓰는 것과 동일하다고 생각하면됨
-> 맨 위의 클로저가 onNext인데 생략된거임.
disposed를 통해 메모리를 관리
-> 항상 마지막에 씀
modelSelected는 기존의 didSelectRowAt을 생각하면됨
-> items의 요소를 사용할건데 String이므로 String.self가 들어간 것
map으로 데이터를 변화시킴
-> 이부분이 없으면 눌렀을때 그냥 items의 요소만 출력될텐데, map으로 인해서 '를 클릭'부분까지 추가
마지막 bind로 simpleLabel의 text에 표시
-> simpleLabel.text와 simpleLabel.rx.text는 다름. 그래서 rx를 사용할떈 신경써주기
PickerView
func setPickerView() {
let items = Observable.just([
"영화",
"애니메이션",
"드라마",
"기타"
])
items
.bind(to: simplePickerView.rx.itemTitles) { (row, element) in
return element
}
.disposed(by: disposeBag)
simplePickerView.rx.modelSelected(String.self)
//bind로 했을때는 배열로 들어가는걸 확인해야함
// .map { "\($0)"}
// .subscribe(onNext: { value in
// print(value)
// })
// .disposed(by: disposeBag)
.map { $0.description }
.bind(to: simpleLabel.rx.text)
.disposed(by: disposeBag)
}
마찬가지로 just로 값을 emit하는 형태
items의 bind에서 row가 아닌 pickerView에는 items 요소들의 string을 띄울 것이므로 element bind
테이블뷰와 같이 modelSelected로 pick되면 레이블에 텍스트를 띄움
Switch
func setSwitch() {
//just와 of으 ㅣ결과는 같음
//just와 of는 오퍼레이턴데 비교하기좋음
Observable.of(false) // 빌드시 값 false로 주는거
.bind(to: simpleSwitch.rx.isOn)
.disposed(by: disposeBag)
}
just와 of는 같다고 봐도됨
근데 of는 여러개의 값을 줄 수 있는데, 하나의 값인 경우에는 just와 같음
그래서 위의 경우엔 switch의 초기값을 주는 코든데 하나의 값이 false만 주므로 just를 사용하는게 나은듯
텍스트필드
func setSign() {
//ex. 텍스트필드1, 2 -> 레이블에 보여줌(옵저버블은 텍스트필드, 옵저버는 레이블) 두 가지를 관찰해서 하나의 레이블에 표시. 레이블은 실패할일없으니까 bind
Observable.combineLatest(signName.rx.text.orEmpty, signEmail.rx.text.orEmpty) { value1, value2 in //rx에서 알려주느 text는ㄴ 옵셔널타입. orEmpty는 옵셔널해제되고 nil일때는 emptyString으로 반환
"name은 \(value1)이고, 이메일은 \(value2)입니다."
} //소스중에 하나라도 바뀌면 이벤트 감지, collection에는 소스 넣으면되는듯
.bind(to: simpleLabel.rx.text)
.disposed(by: disposeBag)
signName.rx.text.orEmpty //여기까진 string. 데이터의 흐름 stream을 바꾼다 => UITextField -> Reactive -> String? -> String -> Int -> bool
.map { $0.count } //int형으로 변환
.map { $0 < 4 } //Bool타입으로
.bind(to: signEmail.rx.isHidden, signButton.rx.isHidden) // isHidden이 bool타입이라. 옵저버 여러개 가능
.disposed(by: disposeBag)
signEmail.rx.text.orEmpty
.map { $0.count > 4 }
.bind(to: signButton.rx.isEnabled)
.disposed(by: disposeBag)
signButton.rx.tap //tap은 touchupinside라고 생각
.subscribe { _ in //bind가 아닌이유는 탭이라고 호출하면 컨트롤이벤트가 Void라서 bind해줄만한건 따로없음. 즉, 데이터 스트림을 바꾸지않는이상 bind를 해주지않음
self.showAlert()
}
.disposed(by: disposeBag)
}
combineLatest로 받은 파라미터중 하나라도 변하면 이벤트 감지
orEmpty -> 옵셔널 해제하고, nil인 경우엔 empty string 반환
signName의 텍스트도 map으로 타입을 바꿔주는 것
UITextField -> Reactive(rx) -> String? -> String -> Int -> Bool
bind로 이메일 텍스트필드의 isHidden관리
button에서의 tap은 touchUpInside라고 생각하면됨
-> 여기서 bind를 안쓴건 탭에 대한 컨트롤이벤트가 Void라서 딱히 만질 데이터가 없음
repeat
특정 요소를 반복
take를 이용해 횟수를 정할 수도 있음
-> 네트워킹 같은 로직에서 몇 번 실패하면 alert띄우기 등 가능
Observable.repeatElement("123") //특정 요소 반복. 빌드하지말기. take로 반복수 조절가능
.take(1) //약간 몇번 시도하고 안되면 에러로 보내는등 행동할수있음
.subscribe { value in
print("repeat - \(value)")
} onError: { error in
print("repeat - \(error)")
} onCompleted: {
print("repeat completed")
} onDisposed: {
print("repeat disposed")
}
.disposed(by: disposeBag)
Interval
//화면전환돼도 얘는 계속 살아있기떄문에 수동으로 dispose가 동작하게 해야함. complete나 error가 뜨면 자동으로 호출되는데 안뜨면 수동으로해야함
let intervalOb = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.subscribe { value in
print("interval - \(value)")
} onError: { error in
print("interval - \(error)")
} onCompleted: {
print("interval completed")
} onDisposed: {
print("interval disposed")
}
//.disposed(by: disposeBag)
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
intervalOb.dispose()
}
위의 코드는 1초마다 print가 실행되는 코드
근데 dispose되지않으면 계속해서 살아있음
-> 수동으로 dispose가 동작하게해야함
just, of, from
let itemsA = [3.3, 4.0, 5.0, 2.0, 3.6, 4.8]
let itemsB = [2.3, 2.0, 1.3]
Observable.just(itemsA) //element에 대한걸 하나만 받을 수 있음
.subscribe { value in
print("just - \(value)")
} onError: { error in
print("just - \(error)")
} onCompleted: {
print("just completed")
} onDisposed: {
print("just disposed")
}
.disposed(by: disposeBag)
Observable.of(itemsA, itemsB) //of는 여러개를 묶어서 전달가능. 그래서 객체가 하나면 just와 차이업승ㅁ
.subscribe { value in
print("of - \(value)")
} onError: { error in
print("of - \(error)")
} onCompleted: {
print("of completed")
} onDisposed: {
print("of disposed")
}
.disposed(by: disposeBag)
Observable.from(itemsA) //하나씩 순환
.subscribe { value in
print("from - \(value)")
} onError: { error in
print("from - \(error)")
} onCompleted: {
print("from completed")
} onDisposed: {
print("from disposed")
}
.disposed(by: disposeBag)
말했듯이 of는 여러 개를 묶어서 전달할 수 있음
그래서 객체가 하나면 just와 차이없음.
from은 하나씩
range
func rangeTest() {
Observable.range(start: 1, count: 10)
.subscribe { print($0) }
.disposed(by: disposeBag)
}
for문이라고 생각해도 될듯
generate
func generateTest() {
Observable.generate(
initialState: 10,
condition: { $0 < 15 },
iterate: { $0 + 1 }
)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
while문이랑 동일함
initialState의 값부터 condition이 false인 경우까지 iterate에 해당하는 코드를 실행
-> 위의 코드는 10부터 15미만까지 1씩 증가해서 출력
deffered
func defferedTest() {
var count = 1
let deferredSequence = Observable<String>.deferred {
print("Creating \(count)")
count += 1
return Observable.create { observer in
print("Emitting...")
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐵")
return Disposables.create()
}
}
deferredSequence
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
deferredSequence
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
옵저버블의 생성을 지연
subscribe되기전까지 생성하지않음
언제 쓸 지는 모르겠음.
과제하면서 알게된거
subscribe에서 onNext가 있는 상태에서 작성한거랑 없는 상태에서 클로저 작성한거랑 다름
-> 생략된게 아니라 다른 메서드
-> 없는 걸로 하면 Next까지 같이 출력
pickerView에서 int와 item이 있는데 int는 정확히 뭔지 모르겠음.
row같은데 제대로 출력되지도 않음
'TIL' 카테고리의 다른 글
[TIL] 2022 / 10 / 26 - Rx (0) | 2022.10.26 |
---|---|
[TIL] 2022 / 10 / 25 - Rx (0) | 2022.10.25 |
[TIL] 2022 / 10 / 13 - Realm Migration (1) | 2022.10.13 |
[TIL] 2022/ 10 / 12 - Remote Notification, Method Swizzling (0) | 2022.10.12 |
[TIL] 2022 / 09 / 06 (0) | 2022.09.06 |
- Total
- Today
- Yesterday