How to implement a basic UITextField input + UIButton action scenario using ReactiveCocoa 3?

时光毁灭记忆、已成空白 提交于 2019-12-03 01:27:06

From the ReactiveCocoa/CHANGELOG.md:

An action must indicate the type of input it accepts, the type of output it produces, and what kinds of errors can occur (if any).

So currently there is no way to define an Action without an input.

I suppose you could declare that you don't care about input by making it AnyObject? and creating CocoaAction with convenience initialiser:

cocoaAction = CocoaAction(viewModel.action)

Additional remarks

  • I dont't like using AnyObject? instead of Bool for validatedTextProducer. I suppose you preferred it because binding to the buttonEnabled property requires AnyObject?. I would rather cast it there though, instead of sacrificing type clarity of my view model (see example below).

  • You might want to restrict execution of the Action on the view model level as well as UI, e.g.:

    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
                //...
            }
        }
    }
    

    And binding to buttonEnabled:

    func bindSignals() {
        buttonEnabled <~ viewModel.action.enabled.producer |> map { $0 as AnyObject }
        //...
    }
    

If you take a look at Colin Eberhardt blog post on ReactiveCocoa 3 there's a very nice approach to this problem.

Basically because it's still in beta there's no extension on UIView that makes those properties easy to use with RAC3 but you can add them easily. I would recommend adding a UIKit+RAC3.swift extension and adding them as you need:

import UIKit
import ReactiveCocoa

struct AssociationKey {
    static var hidden: UInt8 = 1
    static var alpha: UInt8 = 2
    static var text: UInt8 = 3
    static var enabled: UInt8 = 4
}

func lazyAssociatedProperty<T: AnyObject>(host: AnyObject,
    key: UnsafePointer<Void>, factory: ()->T) -> T {
        var associatedProperty = objc_getAssociatedObject(host, key) as? T

        if associatedProperty == nil {
            associatedProperty = factory()
            objc_setAssociatedObject(host, key, associatedProperty,
                UInt(OBJC_ASSOCIATION_RETAIN))
        }
        return associatedProperty!
}

func lazyMutableProperty<T>(host: AnyObject, key: UnsafePointer<Void>,
    setter: T -> (), getter: () -> T) -> MutableProperty<T> {
        return lazyAssociatedProperty(host, key) {
            var property = MutableProperty<T>(getter())
            property.producer
                .start(next: {
                    newValue in
                    setter(newValue)
                })
            return property
        }
}

extension UIView {
    public var rac_alpha: MutableProperty<CGFloat> {
        return lazyMutableProperty(self, &AssociationKey.alpha, { self.alpha = $0 }, { self.alpha  })
    }

    public var rac_hidden: MutableProperty<Bool> {
        return lazyMutableProperty(self, &AssociationKey.hidden, { self.hidden = $0 }, { self.hidden  })
    }
}

extension UIBarItem {
    public var rac_enabled: MutableProperty<Bool> {
        return lazyMutableProperty(self, &AssociationKey.enabled, { self.enabled = $0 }, { self.enabled  })
    }
}

That way you simply replace the RAC = RACObserve logic by (for example):

var date = MutableProperty<NSDate?>(nil)
var time = MutableProperty<Int?>(nil)

let doneItem = UIBarButtonItem()
doneItem.rac_enabled <~ date.producer
        |> combineLatestWith(time.producer)
        |> map { return $0.0 != nil && $0.1 != nil }

Again this is all taken from his blog post which far more descriptive than this answer. I highly recommend anyone interested in using RAC 3 reads his amazing posts and tutorials:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!