RxSwift - withUnretained

JSLee·2022년 3월 11일
0

weak

우리는 지금까지 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 를 정식 채택 하였습니다.

withUnretained

viewModel.importantInfo
  .withUnretained(self) // (Object, Element) 듀플 반환
  .subscribe(onNext: { owner, info in 
    owner.doImportantTask(with: info)
  })
  .disposed(by: disposeBag)

위 코드와 같은 로직을 수행하는 코드 입니다.

하지만 코드가 훨씬 깔끔해 보이고 RxSwift 다운 체인 형식의 코드 작성에 효율이 올라갔습니다.

Error Handling

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 를 사용할수 없죠

하지만 아예 방법이 없진 않습니다.

subscribe(with:)

매개변수 사용

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)

매개변수 사용 X

observable
        .subscribe(with: self) {
            $0.doSomething($1)
        } onError: {
            $0.handleError($1)
        } onCompleted: {
            $0.handleDone()
        }
        .disposed(by: bag)

여기서 달러사인 $0은 자신에 대한 참조 이고 $1 은 값 , error 입니다.

withUnretained , FlatMap 사용

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은 옵저버블을 직접 반환하며, 이는 차례로 스트림에서 모델 튜플을 삭제합니다.

profile
iOS/Android/FE/BE

0개의 댓글