I have an app where I am trying to implement RxSwift using MVVM.
I have the SignInViewModel where I am doing the validation and I am updating the login observable with the rest response boolean that I am listening to .
In the controller class when ever the validations pass the login button gets enabled.
In a similar manner I want to be able to start a spinner on click of the button and dismiss when the user receives a response.
When I try to listen to the loginObservable in from view model in the controller class. it does not hit the bind block.
I am not able to figure out what the problem is.
Any help will be appreciated
Following is my SignInViewModel
class SignInViewModel {
let validatedEmail: Observable<Bool>
let validatedPassword: Observable<Bool>
let loginEnabled: Observable<Bool>
let loginObservable: Observable<Bool>
init(username: Observable<String>,
password: Observable<String>,
loginTap: Observable<Void>) {
self.validatedEmail = username
.map { $0.characters.count >= 5 }
.shareReplay(1)
self.validatedPassword = password
.map { $0.characters.count >= 2 }
.shareReplay(1)
self.loginEnabled = Observable.combineLatest(validatedEmail, validatedPassword ) { $0 && $1 }
let userAndPassword = Observable.combineLatest(username, password) {($0,$1)}
self.loginObservable = loginTap.withLatestFrom(userAndPassword).flatMapLatest{ (username, password) in
return RestService.login(username: username, password: password).observeOn(MainScheduler.instance)
}
}
}
Following is the moyaRequest class
final class MoyaRequest{
func signIn(userData: Creator) -> Observable<Response> {
return provider.request(.signIn(userData))
.filter(statusCode: 200)
}
}
Following is my RestService class
class RestService:NSObject {
static var moyaRequest = MoyaRequest()
static var disposeBag = DisposeBag()
static func login(username: String, password: String) -> Observable<Bool> {
let userData = Creator()
userData?.username = username
userData?.password = password
print("Username password", userData?.username, userData?.password)
return Observable.create { observer in moyaRequest.signIn(userData: userData!).subscribe{ event -> Void in
switch event {
case .next(let response):
print("Response",response)
case .error(let error):
let moyaError: MoyaError? = error as? MoyaError
let response: Response? = moyaError?.response
let statusCode: Int? = response?.statusCode
print("Sample Response code error" + String(describing: statusCode))
default:
break
}
}
return Disposables.create()
}
}
}
I am trying to bind the view model in the controller class.
class SignInViewController: UIViewController{
let disposeBag = DisposeBag()
@IBOutlet weak var passwordTextfield: UITextField!
@IBOutlet weak var usernameTextfield: UITextField!
private var viewModel : SignInViewModel!
@IBOutlet weak var signInButton: UIButton!
override func viewDidLoad() {
setUpRxViewModel()
}
func setUpRxViewModel(){
self.viewModel = SignInViewModel(username: self.usernameTextfield.rx.text.orEmpty.asObservable(),
password: self.passwordTextfield.rx.text.orEmpty.asObservable(),
loginTap: self.signInButton.rx.tap.asObservable())
self.viewModel.loginEnabled.bind{ valid in
self.signInButton.isEnabled = valid
}.addDisposableTo(disposeBag)
self.viewModel.loginObservable.bind{ input in
print("Login Clicked")
}.addDisposableTo(disposeBag)
}
}
In your login
method you are not dispatching any events to your observer. It should be:
case .next(let response):
observer.on(.next(true))
print("Response",response)
case .error(let error):
observer.on(.error(error))
//or observer.on(.next(false)) if you intend to use Bool as indicator of operation success which is a very bad idea.
let moyaError: MoyaError? = error as? MoyaError
let response: Response? = moyaError?.response
let statusCode: Int? = response?.statusCode
furthermore I recommend you use RxMoyaProvider
everywhere if you are using Moya with RxSwift. Using Observable.create
usually means you are doing something wrong.
You also shouldn't filter off events based on status code at the level of network request because if something goes wrong you are not going to receive any event in your chain.