对MVVM中的信号做出反应?

时间:2017-03-26 18:13:43

标签: swift mvvm reactive-programming reactive-cocoa rx-swift

在我第一次设置登录视图并使用Reactive编程时 - 我无法从我的ViewContoller生成一个Signal,它会在我的视图中通过包含我的表单验证的Bool警告我的observeValues侦听器:< / p>

查看文件

viewModel.outputs.loginSuccess
    .observeValues { [weak self] value in
        print(value)
}

每次更改电子邮件或密码文本字段时,我的当前代码loginSuccess都会触发(我的视图文件中有.addTarget,我的模型视图文件中的MutableProperites更新)。我遇到问题的地方是为tryLogin创建一个仅在按下登录按钮时发出的信号,然后映射我的表单验证(emailAndPassword.map(isValid)),我可以在我的视图文件中对其进行响应。 / p>

模型视图文件

import ReactiveCocoa
import ReactiveSwift
import Result

public protocol LoginViewModelInputs {

    /// String value of email textfield text
    func emailChanged(_ email: String?)

    /// String value of password textfield text
    func passwordChanged(_ password: String?)

    /// Call when login button is pressed.
    func loginButtonTapped()
}

public protocol LoginViewModelOutputs {

    /// Emits on login.
    var loginSuccess: Signal<(Bool), NoError> { get }
}

public protocol LoginViewModelType {
    var inputs: LoginViewModelInputs { get }
    var outputs: LoginViewModelOutputs { get }
}

public final class LoginViewModel: LoginViewModelType, LoginViewModelInputs,     LoginViewModelOutputs {

    public init() {

        let emailAndPassword = Signal.combineLatest(
            self.emailChangedProperty.signal.skipNil(),
            self.passwordChangedProperty.signal.skipNil()
        )

        let tryLogin = loginButtonTappedProperty.map {
            emailAndPassword.map(isValid)
        }

        self.loginSuccess = tryLogin.value
    }

    fileprivate let emailChangedProperty = MutableProperty<String?>(nil)
    public func emailChanged(_ email: String?) {
        self.emailChangedProperty.value = email
    }
    fileprivate let loginButtonTappedProperty = MutableProperty()
    public func loginButtonTapped() {
        self.loginButtonTappedProperty.value = ()
    }
    fileprivate let passwordChangedProperty = MutableProperty<String?>(nil)
    public func passwordChanged(_ password: String?) {
        self.passwordChangedProperty.value = password
    }

    public let loginSuccess: Signal<(Bool), NoError>

    public var inputs: LoginViewModelInputs { return self }
    public var outputs: LoginViewModelOutputs { return self }
}

func isValid(email: String, password: String) -> Bool {
    return !email.characters.isEmpty && !password.characters.isEmpty
}

感谢任何帮助。我没有找到很多关于信号或观察者的好文档,但我可能没有找到合适的位置。

2 个答案:

答案 0 :(得分:1)

Here是使用RAC构建的输入表单的示例。

它有点过时了 - 本例中以rex_开头的所有内容(这是UI绑定的外部扩展库,现在直接集成到RAC中)现在更改为.reactive. < / p>

  • UITextField.rex_textSignal现在是UITextField.reactive.continuousTextValues
  • UIButton.rex_pressed现在是UIButton.reactive.pressed

您可以实施实际的身份验证操作,而不是将凭据保存到NSUserDefaults中的authenticationAction

BTW:您永远不应将用户凭据(尤其是密码)保存到NSUserDefaults。请改用钥匙串!

关键点是

  • 在viewModel中使用MutableProperty来存储当前输入值
  • 使用界面元素上的.reactive属性和绑定操作符<~将viewModel绑定到输入(viewModel.attribute <~ textField.reactive.continuouseTextValues
  • 使用Action按需执行实际工作
  • 通过ActionCocoaAction
  • button.reactive.pressed = CocoaAction(viewModel.loginAction)绑定到UIControl

希望能帮到你!

答案 1 :(得分:1)

以下是我采取的方法

  • 根据用户名和密码输入的验证启用和禁用loginButton
  • 将登录操作绑定到按钮事件

以下是代码:

let usernameField: UITextField
let passwordField: UITextField
let loginEnabled: MutableProperty<Bool>
let loginButton: UIButton

loginEnabled <~
    Signal.combineLatest(
        usernameField.reactive.controlEvents(.allEditingEvents),
        passwordField.reactive.controlEvents(.allEditingEvents))
    .map { _ -> Bool in
        return validation()
    }

// only allow button to be pressed when validation succeeds
loginButton.reactive.isEnabled <~ loginEnabled

// login action
let loginAction = Action<(String?,String?),Void,NoError> { (username, password) -> SignalProducer<Void, NoError> in
    // replace with login procedure
    return SignalProducer.empty
}

// bind button event to login action
loginButton.reactive.pressed = CocoaAction(loginAction, input: 
    (usernameField.text, passwordField.text))