Rxswift 结合多个可观察量的奇怪行为

Rxswift strange behaviour combining multiple observables

我对响应式编程还很陌生,所以我仍然很难理解它是如何工作的。

我正在尝试做的事情: 当用户点击 signUpButton (signUpTrigger) 时,会触发多个 observable。我正在结合他们的结果并生成新的可观察值 (signUp)。

发生了什么:当用户点击 signUpButton (signUpTrigger) 时,我从 putProfileImage 和 createUser 可观察对象获取数据,但没有从 signUp 可观察对象获取数据。我认为可以编写逻辑来使其工作,但我自己正在努力想出它。

020-08-31 22:28:36.457: signUp -> subscribed
2020-08-31 22:28:36.458: createUser -> subscribed
2020-08-31 22:28:36.459: createProfileImage -> subscribed
2020-08-31 22:28:48.843: createUser -> Event next(<FIRAuthDataResult: 0x6000036fd860>)
2020-08-31 22:28:48.988: createProfileImage -> Event next(https://firebasestorage.googleapis.com/v0/b/primeval-gear-236918.appspot.com/o/profile_images%2F8173E542-93DD-4594-BAA8-705A17227F1B?alt=media&token=85c68612-bea4-41a5-a680-88a61c2e8989)

下面的所有 Viewmodel 和 VC 代码:

final class SignUpViewModel: ViewModelType {
    
    func transform(input: Input) -> Output {
        
        let auth = Auth.auth()
        let filename = NSUUID().uuidString
  
        
        let storageRef = Storage
            .storage()
            .reference()
            .child("profile_images")
            .child(filename)
            .rx
        
        let databaseRef = Database
            .database()
            .reference()
            .child("users")
            .rx
        
        let putProfileImage = input.signUpTrigger
            .withLatestFrom(input.profileImage)
            .flatMap { data in
                storageRef.putData(data)
            }.flatMap { _ in
            storageRef.downloadURL()
        }.debug("createProfileImage")
        
        let createUser = input.signUpTrigger
            .withLatestFrom(Observable.combineLatest(input.email, input.password))
            .flatMap { (email,password) in
                auth.rx.createUser(withEmail: email, password: password)
        }.debug("createUser")
        
        let signUp = input.signUpTrigger
            .withLatestFrom(Observable.combineLatest(createUser, putProfileImage, input.username))
            .flatMap { createUserResult, url, username -> Observable<DatabaseReference> in
                let profileImageUrl = url.absoluteString
                let uid = createUserResult.user.uid
                let dictionary = ["username": username, "profileImageUrl": profileImageUrl]
                let values = [uid: dictionary]
                return databaseRef.updateChildValues(values)
                    .asObservable()
        }.debug("signUp")
        
    
        let canSignUp = Observable.combineLatest(input.username, input.password, input.email)
        { username, password, email in
            return !username.isEmpty && !password.isEmpty && !email.isEmpty
        }
        
        return Output(canSignUp: canSignUp, isLoading: isLoading, signUp: signUp)
    }
    
}

class SignUpViewController : UIViewController {
    
    var disposeBag = DisposeBag()
    
    @Injected var signUpView : SignUpView
    @Injected var viewModel : SignUpViewModel
    
    lazy var plusPhotoButton = signUpView.plusPhotoButton
    lazy var signUpButton = signUpView.signUpButton
    lazy var emailTextField = signUpView.emailTextField
    lazy var usernameTextField = signUpView.usernameTextField
    lazy var passwordTextField = signUpView.passwordTextField
    
    override func loadView() {
        view = signUpView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupBindings()
    }
    
    func setupBindings() {
  
        let input = SignUpViewModel.Input(
            username: usernameTextField.rx.text.orEmpty.asObservable(),
            password: passwordTextField.rx.text.orEmpty.asObservable(),
            email: emailTextField.rx.text.orEmpty.asObservable(),
            signUpTrigger: signUpButton.rx.tap.asObservable(),
        )
        
        let output = viewModel.transform(input: input)
        
        output.canSignUp
            .bind(to:signUpButton.rx.signUpEnabled)
            .disposed(by: disposeBag)
        
        output.signUp
            .bind(onNext: { result in
                print(result)
            }).disposed(by:disposeBag)
     
        
    }
}

您现在拥有代码的方式:

let signUp = input.signUpTrigger
    .withLatestFrom(Observable.combineLatest(createUser, putProfileImage, input.username))

updateChildValues(_:) 将在 signUpTrigger 触发后立即发生,但您不希望这样。您希望它在其他三个事件的结果发生时发生。

所以你想要这样的东西:

let signUp = Observable.zip(createUser, putProfileImage, input.signUpTrigger.withLatestFrom(input.username))
    .flatMap { createUserResult, url, username -> Observable<DatabaseReference> in
        let profileImageUrl = url.absoluteString
        let uid = createUserResult.user.uid
        let dictionary = ["username": username, "profileImageUrl": profileImageUrl]
        let values = [uid: dictionary]
        return databaseRef.updateChildValues(values)
            .asObservable()
        }
        .debug("signUp")