Swift 仅在rx_抽头事件时调用方法

Swift 仅在rx_抽头事件时调用方法,swift,rx-swift,Swift,Rx Swift,我对rxswift有一个基本问题。 我有一个登录屏幕,当且仅当我点击登录按钮时,它应该在视图模型内调用我的登录方法 loginButton.rx_tap .doOn({[unowned self] _ in self.loginButton.enabled = false }) .flatMap({[unowned self] in self.loginModel.login() }) .su

我对rxswift有一个基本问题。 我有一个登录屏幕,当且仅当我点击登录按钮时,它应该在视图模型内调用我的登录方法

        loginButton.rx_tap
        .doOn({[unowned self] _ in
            self.loginButton.enabled = false
        })
        .flatMap({[unowned self] in self.loginModel.login() })
        .subscribeNext({ [weak self] login  in
            self?.loginButton.enabled = true

            guard login?.result == 1 else {
                self?.showErrorWithMessage(login!.message)
                return;
            }

           // Logged in!
        })
        .addDisposableTo(disposeBag)
看起来很好,但是如果由于凭据无效而导致登录失败,并且用户重新开始键入文本字段,则会再次触发对loginModel.login()的调用

在视图模型内部,还有:

    var credentials : Driver<(String, String)> {
    return Driver.combineLatest(userNameDriver.distinctUntilChanged(), passwordDriver.distinctUntilChanged()) { usr, pwd in
        return (usr, pwd)
    }
}

var usrValid : Driver<Bool> {
    get {
        return userNameDriver
            .throttle(0.5)
            .filterEmpty()
            .distinctUntilChanged()
            .map { ($0.rangeOfString("@") != nil) || ($0.utf8.count == 0) }
    }
}

var pwdValid : Driver<Bool> {
    get {
        return passwordDriver
        .throttle(0.5)
        .filterEmpty()
        .distinctUntilChanged()
        .map { ($0.utf8.count > 5) || ($0.utf8.count == 0) }
    }
}

var usernameBorderColor : Observable<UIColor>!
var passwordBorderColor : Observable<UIColor>!

var credentialValid : Driver<Bool> {
    return Driver.combineLatest(usrValid, pwdValid) { usr, pwd in
        return (usr && pwd)
    }
}
var凭证:驱动程序{
返回Driver.combineTest(userNameDriver.distinctUntilChanged(),passwordDriver.distinctUntilChanged()){usr,pwd in
返回(usr、pwd)
}
}
var usrValid:驱动程序{
得到{
返回用户名驱动程序
.节气门(0.5)
.filterrempty()
.distinctUntilChanged()
.map{($0.rangeOfString(“@”)!=nil)| |($0.utf8.count==0)}
}
}
var pwdValid:驱动程序{
得到{
返回密码驱动程序
.节气门(0.5)
.filterrempty()
.distinctUntilChanged()
.map{($0.utf8.count>5)| |($0.utf8.count==0)}
}
}
var usernameBorderColor:可观察!
var passwordBorderColor:可观察!
var credentialValid:驱动程序{
返回驱动程序。组合测试(usrValid,pwdValid){usr,pwd-in
返回(usr和pwd)
}
}
有人能帮我吗?
谢谢

我审阅了你的项目。问题在于ViewModel的体系结构不是很好。我使用来自的示例修复了一个。请参见下面的新视图模型:

class LoginViewModel
{
let validatedUsername: Driver<Bool>
let validatedPassword: Driver<Bool>

// Is login button enabled
let loginEnabled: Observable<Bool>

// Has user log in
let loggedIn: Observable<Login>

var usernameBorderColor : Observable<UIColor>
var passwordBorderColor : Observable<UIColor>

init(input: (
    userName: Driver<String>,
    password: Driver<String>,
    loginClick: Observable<Void>
    ),
    dependency: (
    RxMoyaProvider<APIProvider>
    )
    ) {

    let credentials = Driver.combineLatest(input.userName, input.password) { ($0, $1) }

    validatedUsername = input.userName.map { $0.rangeOfString("@") != nil }
    validatedPassword = input.password.map { $0.utf8.count > 5 }

    usernameBorderColor = validatedUsername.asObservable()
        .map{valid in
            return valid ? UIColor.clearColor() : UIColor.redColor()
    }
    passwordBorderColor = validatedPassword.asObservable()
        .map{valid in
            return valid ? UIColor.clearColor() : UIColor.redColor()
    }

    loginEnabled = Observable.combineLatest(
        validatedUsername.asObservable(),
        validatedPassword.asObservable()
    )   { username, password in
        username &&
        password
        }
        .distinctUntilChanged()
        .shareReplay(1)

    loggedIn = input.loginClick.withLatestFrom(credentials)
        .flatMap { username, password -> Observable<Login> in
            var resultLogin : Login
            if username == "test@" && password == "123123" {
                resultLogin = Login(result: 1, message: "OK")
            }
            else {
                resultLogin = Login(result: 0, message: "KO")
            }

            return Observable.create { observer in
                observer.onNext(resultLogin)
                observer.onCompleted()

                return AnonymousDisposable {

                }
            }
        }
        .retry()
        .shareReplay(1)
    }
}

我创建了pull请求,您必须接受它并查看完整的源代码。祝你好运

嘿!我无法复制这个bug,因为它对我来说并不明显。您可以通过登录Github创建简单的项目吗?我将克隆它,帮助您修复它,并在本主题中编写解决方案。在这里,您可以找到该项目:我添加了一个示例,只是为了查看问题。如果您在文本字段中输入user并通过,然后点击login,您将得到一个基于结果的回复…例如,您写入了错误的凭据并点击login,您将在SubscribeText中获得结果,但是,如果你尝试写入新凭据,信号会再次启动…我只想取消以前的信号,并在点击按钮登录时启动新信号。非常感谢,我刚刚合并,今晚我查看了你的代码:)@Svyatoslav这真的帮了我大忙。谢谢你如此详尽的回答。@Danny谢谢。很乐意帮忙。
let viewModel = LoginViewModel(
        input: (
            userName: userTextField.rx_text.asDriver(),
            password: pwdTextField.rx_text.asDriver(),
            loginClick: loginButton.rx_tap.asObservable()
        ),
        dependency: (
            apiProvider
        )
    )

    viewModel.loginEnabled
        .subscribeNext { [weak self] valid  in
            self?.loginButton.enabled = valid
            self?.loginButton.alpha = valid ? 1.0 : 0.5
        }
        .addDisposableTo(self.disposeBag)

    viewModel.loggedIn
        .subscribeNext { login  in

            print(login.message)

            guard login.result == 1 else {
                // Show error
                return;
            }


            }
        .addDisposableTo(disposeBag)