우리는 지금까지 RxSwift 에서 self 에 대한 약함참조(weak) 하기 위해서
viewModel.importantInfo
.subscribe(onNext: { [weak self] info in
guard let self = self else { return }
self.doImportantTask(with: info)
})
.disposed(on: disposeBag)
이러한 형태의 작업을 했었습니다.
실제 작업을 하다보면 이런 형태의 코드를 자주 작성해야 하고
그로인해 코드의 가독성과 효율이 저하 됩니다.
그래서 보통의 RxSwift 작업중 self 에 대한 약한 참조를
RxSwiftExt(RxSwift 에 존재하지 않는 Operator 를 추가한 오픈 소스) 안에 있는 withUnretained 를 사용하여 해소했습니다.
RxSwift 는 6 release 부터 withUnretained Operator 를 정식 채택 하였습니다.
viewModel.importantInfo
.withUnretained(self) // (Object, Element) 듀플 반환
.subscribe(onNext: { owner, info in
owner.doImportantTask(with: info)
})
.disposed(by: disposeBag)
위 코드와 같은 로직을 수행하는 코드 입니다.
하지만 코드가 훨씬 깔끔해 보이고 RxSwift 다운 체인 형식의 코드 작성에 효율이 올라갔습니다.
observable
.withUnretained(self)
.subscribe { (owner, string) in
owner.doSomething(string)
} onError: { [weak self] (error) in
guard let self = self else { return }
self.handleError(error)
} onCompleted: { [weak self] () in
guard let self = self else { return }
owner.handleDone()
}
.disposed(by: bag)
Error , Completed 에 대한 핸들링에 대해선 다시 [weak self] 를 사용하는 모습을 볼수 있습니다.
이러한 이유에 대해선 RxSwift 에 Event 에 대해 알아볼 필요가 있습니다.
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
Event 는 열거형 즉 enum 입니다.
map,flatMap 을 통해 next 는 우리가 원하는 요소의 유형으로 변경이 가능했지만
error 와 completed 는 RxSwift 에서의 기존 정의가 있어 변경할수 없습니다.
그래서 체인 아래에 있는 Reference 를 subscribe 에게 전달할 방법은 없습니다.
그래서 error , completed 는 withUnretained 를 사용할수 없죠
하지만 아예 방법이 없진 않습니다.
observable
.subscribe(with: self) { (owner, string) in
owner.doSomething(string)
} onError: { (owner, error) in
owner.handleError(error)
} onCompleted: { (owner) in
owner.handleDone()
}
.disposed(by: bag)
observable
.subscribe(with: self) {
$0.doSomething($1)
} onError: {
$0.handleError($1)
} onCompleted: {
$0.handleDone()
}
.disposed(by: bag)
여기서 달러사인 $0은 자신에 대한 참조 이고 $1 은 값 , error 입니다.
func setupLogin(_ submit: Observable<(String?, String?)>) -> Observable<User> {
return submit
.withLatestFrom(credentials)
.withUnretained(self)
.flatMap { (model, creds) -> Observable<String> in
model.authorize(username: creds.0, password: creds.1)
}
.withUnretained(self)
.flatMap { (model, token) -> Observable<User> in
model.user(authorization: token)
}
}
여기에서 withUnretained은(는) 다시 self를 캡처하고 다음 flatMap에 해당 모델에 대한 유효한 참조가 있는지 확인합니다.
그러나 함께 연결 하려면 각각에 대한 문 flatMaps이 필요합니다
. withUnretained각각 flatMap은 옵저버블을 직접 반환하며, 이는 차례로 스트림에서 모델 튜플을 삭제합니다.