typealiasValidationResult=(valid:Bool?,message:String?)funcvalidateUsername(username:String)->Observable<ValidationResult>{ifusername.characters.count==0{returnjust((false,nil))}// this obviously won't beifusername.rangeOfCharacterFromSet(NSCharacterSet.alphanumericCharacterSet().invertedSet)!=nil{returnjust((false,"Username can only contain numbers or digits"))}letloadingValue=(valid:nilasBool?,message:"Checking availabilty ..."asString?)returnAPI.usernameAvailable(username).map{availableinifavailable{return(true,"Username available")}else{return(false,"Username already taken")}}.startWith(loadingValue)}
The wrapped value in Observable is a Bool and String pair.
The first two if clause are for checking empty and illegal characters, respectively. The result will be returned immediately because they are local process.
The last part is a http request which returns result after a short period of time.
If we call ‘validateUsername’ at each event in user input event sequence,
Event sequence will become (V for validation, R for result)
1234567
---+---+-+-----+-----||||VVVV||||R|||R|RR
Note that even validations are called in order, results are returned in random order according to network state. And actually we only need the latest validation’s result.
So we use switch method here. switchlatest is one of switch’s implementation, which will always switch to the latest event occurred and dispose former events. Intro_to_rx_switch.
And shareReplay(1) will keep only 1 allocation even this observer gets new subscriptions later. Rxswift_replay
funcvalidatePassword(password:String)->ValidationResult{letnumberOfCharacters=password.characters.countifnumberOfCharacters==0{return(false,nil)}ifnumberOfCharacters<minPasswordCount{return(false,"Password must be at least \(minPasswordCount) characters")}return(true,"Password acceptable")}funcvalidateRepeatedPassword(password:String,repeatedPassword:String)->ValidationResult{ifrepeatedPassword.characters.count==0{return(false,nil)}ifrepeatedPassword==password{return(true,"Password repeated")}else{return(false,"Password different")}}
Combine these two Observables using combineLatest. It does what exactly it says.
The signup method is just a delayed Observable, which return true or false after 2 seconds,
12345678
funcsignup(username:String,password:String)->Observable<SignupState>{// this is also just a mockletsignupResult=SignupState.SignedUp(signedUp:arc4random()%5==0?false:true)return[just(signupResult),never()].concat().throttle(2,MainScheduler.sharedInstance).startWith(SignupState.SigningUp)}
4. Sign-up Enabled
Just combine validations of user name, password, repeat password