简体   繁体   中英

Rxswift strange behaviour combining multiple observables

I'm quite new to reactive programming so it's still hard to me to comprehend how it works.

What I'm trying to do: when user taps on signUpButton (signUpTrigger), multiple observables are fired. I'm combining their results and producing new observable (signUp).

What happens : when user taps on signUpButton (signUpTrigger), I'm getting data from putProfileImage and createUser observables, but nothing from signUp observable. I assume it's possible to write logic to make it work, but I'm struggling to come up with it myself.

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)

All the Viewmodel and VC code below:

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)
     
        
    }
}

The way you have your code right now:

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

The updateChildValues(_:) will happen immediately after the signUpTrigger fires, but you don't want that. You want it to happen once the results of the other three events occur.

So you want something like this:

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")

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