我正在尝试使用RxSwift实现MVVM登录屏幕,并且遇到了一些困难。

我的登录屏幕(用户,密码,登录按钮)过渡到相机查看屏幕,在该屏幕上,应用程序检查相机许可,如果未批准,则将用户注销并返回登录屏幕。

我的loginAction中有一个LoginViewModel返回Action<Void, LoginResult>,其中LoginResult Result<Bool, Error> ,而我的loginProvider服务返回了Observable<LoginResult>:

struct LoginViewModel {

let sceneCoordinator: SceneCoordinatorType
let loginProvider: LoginProviderType

var usernameText = Variable<String>("")
var passwordText = Variable<String>("")

var isValid: Observable<Bool> {
    return Observable.combineLatest(usernameText.asObservable(), passwordText.asObservable()) { username, password in
        username.count > 0 && password.count > 0
    }
}

init(loginProvider: LoginProvider, coordinator: SceneCoordinatorType) {
    self.loginProvider = loginProvider
    self.sceneCoordinator = coordinator
}

lazy var loginAction: Action<Void, LoginResult> = { (coordinator: SceneCoordinatorType, service: LoginProviderType, username: String, password: String) in
    return Action<Void, LoginResult>(enabledIf: self.isValid) { _ in
        return service.login(username: username, password: password)
            .observeOn(MainScheduler.instance)
            .do(onNext: { result in
                guard let loggedIn = result.value else { return }
                if loggedIn {
                    let cameraViewModel = CameraViewModel(coordinator: coordinator)
                    coordinator.transition(to: Scene.camera(cameraViewModel), type: .modal)
    }
}(self.sceneCoordinator, self.loginProvider, self.companyText.value, self.usernameText.value, self.passwordText.value)
}

一切正常,有效的输入成功登录(loginProvider将请求发送到我的服务器,获取响应并相应地处理所有其他步骤)。

如果用户未授予相机权限,则我的Observable中有一个CameraViewModel,我将其绑定(bind)到CameraViewController,进行订阅,并在需要时注销用户,并使用CocoaAction将 View 弹出回到登录屏幕弹出当前 View (使用场景协调器类)。

问题是,当我转换回到“登录”屏幕后尝试再次登录时,由loginAction发出的元素的订阅未收到任何元素。

这是LoginViewController的代码:
class LoginViewController: UIViewController, BindableType {

var viewModel: LoginViewModel!

private let disposeBag = DisposeBag()

private var loginAction: Action<Void, LoginResult>!

@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var loadingIndicator: UIActivityIndicatorView!


private var usernameObservable: Observable<String> {
    return usernameTextField.rx.text
        .throttle(0.5, scheduler: MainScheduler.instance)
        .map() { text in
            return text ?? ""
    }
}

private var passwordObservable: Observable<String> {
    return passwordTextField.rx.text
        .throttle(0.5, scheduler: MainScheduler.instance)
        .map() { text in
            return text ?? ""
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

}

func bindViewModel() {

    usernameObservable
        .bind(to: viewModel.usernameText)
        .disposed(by: disposeBag)

    passwordObservable
        .debug()
        .bind(to: viewModel.passwordText)
        .disposed(by: disposeBag)

    loginAction = viewModel.loginAction

    loginAction.elements
        .subscribe(onNext: { [weak self] result in
            self?.loadingIndicator.stopAnimating()
            var message = ""
            switch result {
            case  .failure(.unknownError):
                message = "unknown error"
            case .failure(.wrongCredentials):
                message = "wrong credentials"
            case .failure(.serviceUnavailable):
                message = "service unavailable"
            case let .success(loggedIn):
                return
            }
            self?.errorMessage(message: message)
    }).disposed(by: disposeBag)

    loginButton.rx.tap
        .subscribe(onNext: { [unowned self] in
                self.loadingIndicator.startAnimating()
                self.loginAction.execute(Void())
            })
    .disposed(by: disposeBag)

    viewModel.isValid
        .bind(to: loginButton.rx.isEnabled)
        .disposed(by: disposeBag)
}

我看到点击登录按钮会产生点击事件,但是Action本身不再被调用。知道我缺少什么吗?

最佳答案

我不确定ScreenCoordinator的工作方式,但是我将首先将usernameObservable/passwordObservable的创建移动到viewDidLoad(),因为该问题似乎与生命周期相关。另外,您可以添加更多.debug()调用(使用参数可以在日志中区分它们)。

08-16 14:48