【问题标题】:Reacting to a Signal in MVVM?对 MVVM 中的信号做出反应?
【发布时间】:2017-08-19 08:24:46
【问题描述】:

在我第一次设置登录视图并使用响应式编程时 - 我无法从 ViewContoller 生成信号,该信号将在我的视图中使用包含我的表单验证的 Bool 提醒我的 observeValues 侦听器:

查看文件

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

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

模型视图文件

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
}

感谢任何帮助。我没有找到太多好的文档来了解信号或观察者,但可能我没有找到正确的地方。

【问题讨论】:

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


    【解决方案1】:

    这是我将采取的方法

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

    代码如下:

    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))
    

    【讨论】:

      【解决方案2】:

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

      虽然有点过时了 - 本示例中以 rex_ 开头的所有内容(它是 UI Bindings 的外部扩展库,现在直接集成到 RAC 中)现在已更改为 .reactive.

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

      您无需将凭据保存到authenticationAction 中的NSUserDefaults,而是实施您的实际身份验证操作。

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

      关键点是

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

      希望对你有所帮助!

      【讨论】:

      • 谢谢!我昨天刚写了一些保存用户凭证的代码并使用了钥匙串;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-03
      • 2017-05-25
      • 2021-02-18
      相关资源
      最近更新 更多