8

私は Swift と ReactiveCocoa の初心者でもあります。MVVM と Reactive Cocoa v3.0-beta.4 フレームワークを使用して、このセットアップを実装し、新しい RAC 3 フレームワークの基本を学びたいと思います。

テキスト フィールドがあり、検証のためにテキスト入力に 3 文字以上を含める必要があります。テキストが検証に合格すると、下のボタンが有効になります。ボタンがタッチダウン イベントを受け取ったら、ビュー モデルのプロパティを使用してアクションをトリガーしたいと考えています。

現時点では RAC 3.0 ベータ版に関するリソースがほとんどないため、フレームワークの Github リポジトリの QA を読んで、次のことを実装しました。これが私がこれまでに思いついたものです:

ViewModel.swift

class ViewModel {

    var text = MutableProperty<String>("")
    let action: Action<String, Bool, NoError>
    let validatedTextProducer: SignalProducer<AnyObject?, NoError>

    init() {
        let validation: Signal<String, NoError> -> Signal<AnyObject?, NoError> = map ({
            string in
            return (count(string) > 3) as AnyObject?
        })

        validatedTextProducer = text.producer.lift(validation)

        //Dummy action for now. Will make a network request using the text property in the real app. 
        action = Action { _ in
            return SignalProducer { sink, disposable in
                sendNext(sink, true)
                sendCompleted(sink)
            }
        }
    }
}

ViewController.swift

class ViewController: UIViewController {

    private lazy var txtField: UITextField = {
        return createTextFieldAsSubviewOfView(self.view)
    }()

    private lazy var button: UIButton = {
        return createButtonAsSubviewOfView(self.view)
    }()

    private lazy var buttonEnabled: DynamicProperty = {
       return DynamicProperty(object: self.button, keyPath: "enabled")
    }()

    private let viewModel = ViewModel()
    private var cocoaAction: CocoaAction?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.setNeedsUpdateConstraints()

        bindSignals()
    }

    func bindSignals() {
        viewModel.text <~ textSignal(txtField)
        buttonEnabled <~ viewModel.validatedTextProducer

        cocoaAction = CocoaAction(viewModel.action, input:"Actually I don't need any input.")
        button.addTarget(cocoaAction, action: CocoaAction.selector, forControlEvents: UIControlEvents.TouchDown)

        viewModel.action.values.observe(next: {value in
            println("view model action result \(value)")
        })
    }

    override func updateViewConstraints() {
        super.updateViewConstraints()

        //Some autolayout code here
    }
}

RACUtilities.swift

func textSignal(textField: UITextField) -> SignalProducer<String, NoError> {
    return textField.rac_textSignal().toSignalProducer()
        |> map { $0! as! String }
        |> catch {_ in SignalProducer(value: "") }
}

この設定では、ビュー モデルのテキストが 3 文字を超える場合にボタンが有効になります。ユーザーがボタンをタップすると、ビュー モデルのアクションが実行され、戻り値を true として取得できます。ここまでは順調ですね。

私の質問は: ビュー モデルのアクション内で、保存されたテキスト プロパティを使用し、それを使用してネットワーク リクエストを行うようにコードを更新したいと考えています。したがって、View Controller 側からの入力は必要ありません。Action プロパティへの入力を要求しないようにするにはどうすればよいですか?

4

2 に答える 2

4

ReactiveCocoa/CHANGELOG.mdから:

アクションは、受け入れる入力のタイプ、生成する出力のタイプ、および発生する可能性のあるエラーの種類 (存在する場合) を示す必要があります。

Actionそのため、現在、入力なしでを定義する方法はありません。

AnyObject?入力を作成CocoaActionし、便利な初期化子を使用して作成することで、入力を気にしないことを宣言できると思います。

cocoaAction = CocoaAction(viewModel.action)

補足事項

  • forAnyObject?の代わりに使用するのは好きではありません。プロパティへのバインドには. ただし、ビューモデルの型の明確さを犠牲にするのではなく、そこにキャストしたいと思います(以下の例を参照)。BoolvalidatedTextProducerbuttonEnabledAnyObject?

  • Actionビュー モデル レベルおよび UI での実行を制限したい場合があります。たとえば、次のようになります。

    class ViewModel {
    
        var text = MutableProperty<String>("")
        let action: Action<AnyObject?, Bool, NoError>
    
        // if you want to provide outside access to the property
        var textValid: PropertyOf<Bool> {
            return PropertyOf(_textValid)
        }
    
        private let _textValid = MutableProperty(false)
    
        init() {
            let validation: Signal<String, NoError> -> Signal<Bool, NoError> = map { string in
                return count(string) > 3
            }
    
            _textValid <~ text.producer |> validation
    
            action = Action(enabledIf:_textValid) { _ in
                //...
            }
        }
    }
    

    そしてバインディングbuttonEnabled

    func bindSignals() {
        buttonEnabled <~ viewModel.action.enabled.producer |> map { $0 as AnyObject }
        //...
    }
    
于 2015-05-12T11:12:27.477 に答える