简体   繁体   中英

RxSwift properly dispose subscription in closure

I am writing a wrapper around Firebase authentication functions to return Observable and add additional profileIncomplete state. It basically first checks whether a user is logged in, if so, check whether the user's profile is complete. The following is my code, I wonder whether it is okay to subscribe to an observable in Observable.create and, if so, how do I properly dispose the disposable in this case? create a DisposeBag inside the closure?

enum State {
    case loggedIn
    case profileIncomplete
    case notLoggedIn
}

func listenToAuthState() -> Observable<State> {
    return Observable.create { observable in
        let authStateHandle = Auth.auth().addStateDidChangeListener() { [weak self] (_, user) in
            guard let user = user else {
                observable.onNext(.notLoggedIn)
                return
            }
            let disposable = self?.listenToProfileCompleted(uid: user.uid).subscribe(onNext: { (completed) in
                if completed {
                    observable.onNext(.loggedIn)
                    observable.onCompleted()
                } else {
                    observable.onNext(.profileIncomplete)
                }
            })
            // How to dispose the disposable???
        }
        return Disposables.create { 
            Auth.auth().removeStateDidChangeListener(authStateHandle) }
        }
}

func listenToProfileCompleted(uid: String) -> Observable<Bool> { ... }

I think subscribing inside a Observable.create (or inside a different subscribe block) is a code-smell.

It seems you have two separate concerns. stateChanged and profileCompleted .

I would split those into two different methods, having listenToAuthState only in charge of reflecting the result of addStateDidChangeListener , and have a separate one for listenToProfileCompleted .

This will let you have a separate "ready" (or however you want to call it) that can zip the two. Or otherwise use flatMap, if the auth status must change before you listen to the profile completion.

To dispose resource you can add it to DisposeBag. Like below

func listenToAuthState() -> Observable<State> {
    return Observable.create { observable in
        var disposeBag:DisposeBag! = DisposeBag()
        let authStateHandle = Auth.auth().addStateDidChangeListener() { [weak self] (_, user) in
            guard let user = user else {
                observable.onNext(.notLoggedIn)
                return
            }
            let disposable = self?.listenToProfileCompleted(uid: user.uid).subscribe(onNext: { (completed) in
                if completed {
                    observable.onNext(.loggedIn)
                    observable.onCompleted()
                } else {
                    observable.onNext(.profileIncomplete)
                }
            }).disposed(by: disposeBag)
            // How to dispose the disposable???
        }
        return Disposables.create { 
            Auth.auth().removeStateDidChangeListener(authStateHandle)
            disposeBag = nil
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM