이들에 대해 다시 정리해보자. Observable은 관찰이 가능한 데이터의 흐름으로, 비동기 이벤트의 시퀀스를 생성할 수 있는 대상이다. 여기서, 시퀀스는 동기적으로 발생하는 이벤트들의 연속적인 흐름으로 '사용자의 클릭', '네트워크 응답' 같은 것들이 될 수 있다.
예를 들어, 버튼이 있다고 하면 버튼의 흐름을 전파하기 위해서는 버튼을 클릭하는 행위 자체가 observable이 되어야 한다.
Observable은 이벤트가 발생했을 때(ex.버튼 클릭), item을 시퀀스로 emit한다. 여기서 시퀀스로 emit한다는 것은, 이벤트에 대한 항목을 순차적으로 emit한다는 뜻이다. 예를 들어서, 버튼을 여러번 누르면 누른 순서대로 emit한다는 뜻이다.
이렇게 방출된 item은 Observer가 받게 된다. 단, Observable을 구독할 경우에만 item을 받을 수 있다. 여기서 Observer가 Observable을 구독할 수 있게 해주는 메서드가 Subscribe와 bind이다.
Subscribe의 원형은 다음과 같다.
public func subscribe(
onNext: ((Element) -> Void)? = nil,
onError: ((Swift.Error) -> Void)? = nil,
onCompleted: (() -> Void)? = nil,
onDisposed: (() -> Void)? = nil
) -> Disposable
보이는 것처럼 모든 파라미터가 클로저 타입이기 때문에 다음과 같은 것들을 클로저로 같이 넘겨주는 식이다.
onNext: 내가 구독하려는 Observable이 item을 방출 했을 때 어떤 작업을 할 것인지
onError: 만약 해당 Observable이 오류가 발생 했을 때는 어떤 작업을 할 것인지
onCompleted: 더이상 이벤트가 발생하지 않고 종료됐을 때는 어떤 작업을 할 것인지
그래서 다음과 같이 작성해줄 수 있다.
tempButton.rx
.tap
.subscribe(onNext: {
print("subscribe")
}
.disposed(by: disposedBag)
번외)
button.rx.tap 자체가 observable이라고 생각할 수 있는데 button.rx.tap의 타입은 tap event에 대한 Observable이 아니라, 'ControlEvent'이라는 것이다. -> ControlEvent에 대고 구독
예제 코드
// 함수 선언: PickerView 설정을 위한 함수
func setPickerView() {
// Observable 생성. just는 하나의 값만을 방출할 때 사용.
let items = Observable.just([
"영화",
"애니메이션",
"드라마",
"기타"
])
// 바인딩: 생성한 items Observable을 simplePickerView의 itemTitles에 바인딩.
// 각 요소(element)는 pickerView의 각 row의 제목(title)이 됨.
items
.bind(to: simplePickerView.rx.itemTitles) { (row, element) in return element }
.disposed(by: disposeBag) // 생성된 구독을 disposeBag에 추가하여 필요 없어지면 자동으로 메모리 해제
// PickerView 모델 선택 이벤트 처리: 사용자가 pickerView에서 항목을 선택하면,
// 선택된 문자열(String)이 modelSelected를 통해 전달됨.
simplePickerView.rx.modelSelected(String.self)
.map { $0.description } // 맵핑: 선택된 항목의 description(여기서는 그대로 문자열)을 가져옴.
.bind(to: simpleLabel.rx.text) // 바인딩: 선택된 항목의 설명을 simpleLabel의 text 속성에 바인딩.
.subscribe(onNext: { value in
print(value)
})
.disposed(by: disposeBag) // 메모리 관리: 생성된 구독을 disposeBag에 추가하여 필요 없어지면 자동으로 메모리 해제
}
// 함수 선언: 사용자 이름과 이메일 입력을 처리하기 위한 함수
func setSign() {
// combineLatest를 사용하여 signName과 signEmail 텍스트 필드의 입력값을 결합
Observable.combineLatest(signName.rx.text.orEmpty, signEmail.rx.text.orEmpty) { value1, value2 in
// 결합된 입력값(value1, value2)을 사용하여 문자열을 생성하고 반환
return "name은 \(value1)이고, 이메일은 \(value2)입니다"
}
// 생성된 문자열을 simpleLabel의 텍스트 속성에 바인딩
.bind(to: simpleLabel.rx.text)=
.disposed(by: disposeBag)
// signName 텍스트 필드의 입력값에 대해
signName.rx.text.orEmpty // 빈 문자열로 대체 가능한 입력값을 관찰
.map { $0.count < 4 } // 입력된 텍스트의 길이가 4보다 작은지 여부를 평가하여 Bool 값으로 변환
// 계산된 Bool 값을 signEmail과 signButton의 isHidden 속성에 바인딩
.bind(to: signEmail.rx.isHidden, signButton.rx.isHidden)
.disposed(by: disposeBag)
// signButton 버튼의 탭(tap) 이벤트에 대해
signButton.rx.tap
.subscribe { _ in
// 탭 이벤트 발생 시 showAlert 함수 호출
self.showAlert()
}
.disposed(by: disposeBag)
}
참고
SeSAC 메모리스
https://babbab2.tistory.com