RxSwift,绑定Action对UIButton在视图转换后停止生成事件

问题描述 投票:0回答:2

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

我的登录屏幕(用户,密码,登录按钮)转换到摄像机视图屏幕,应用程序检查摄像机权限,如果未批准,则将用户注销并返回登录屏幕。

我在我的loginAction有一个LoginViewModel返回Action<Void, LoginResult>,其中LoginResultResult<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,我绑定到我的CameraViewController,订阅并在需要的情况下,将用户注销并使用CocoaAction将视图弹回登录屏幕弹出当前视图(使用场景协调器类)。

问题是,当我尝试在转换回登录屏幕后再次登录时,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本身会停止被调用。知道我错过了什么吗?

ios swift mvvm action rx-swift
2个回答
1
投票

我不确定ScreenCoordinator是如何工作的,但我会先将usernameObservable / passwordObservable的创建转移到viewDidLoad(),因为问题似乎与生命周期有关。此外,您还可以添加一些.debug()调用(带参数以便能够在日志中区分它们)。


0
投票

发现问题:显然,操作未达到完整状态,因为底层服务上的一个返回了一个在登录视图控制器中订阅的观察者,并且在从摄像机视图控制器返回时没有处理。

我在该服务中添加了一个清除方法,它在返回登录视图时创建一个新的DisposeBag,允许操作完成并释放以供重用。

© www.soinside.com 2019 - 2024. All rights reserved.