问题描述
我对反应式编程非常陌生,所以我仍然很难理解它的工作原理。
我要做什么::当用户点击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)
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,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")