0

I can bind a UITextField.rx.text to a Variable<T>. This works fine when I'm into a "creating" screen. But, supposing I have a already created model and want to edit it on the same screen, how would I do that?

I've also seen a lot about infix operators represented by <-> operator. If I don't have this infix operator configured, I'll always have to do that amount of work to achieve two-way binding?

Here's my code

struct TodoViewModel: CustomDebugStringConvertible {
    let title = Variable<String?>("Initial title")
    let description = Variable<String?>("")
    let dueDate = Variable<Date?>(Date())
    let done = Variable<Bool>(false)

    var debugDescription: String {
        get {
            return
                """
                // ======
                Title: \(self.title.value ?? "Nil")
                Description: \(self.description.value ?? "Nil")
                Due Date: \(String(describing: self.dueDate.value))
                Done: \(self.done.value)
                """
        }
    }
}

// ViewController
class AddTaskTableViewController: UITableViewController {
    @IBOutlet weak var labelTitle: UILabel!
    @IBOutlet weak var txtTitle: UITextField!
    @IBOutlet weak var txtDescription: UITextField!
    @IBOutlet weak var txtDate: UITextField!
    @IBOutlet weak var switchDone: UISwitch!

    var todo = TodoViewModel()
    var disposeBag = DisposeBag()
    let dateFormatter = DateFormatter()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dateFormatter.locale = Locale(identifier: "en-US")

        // Configuring reactivity
        // Binds the UITextField's text value to my Model
        // let _ = self.txtTitle.rx.text.bind(to: self.todo.title).disposed(by: self.disposeBag)

        // ⬆⬇ Makes more sense they both being a two-way binding. This way I can edit and add items using the same screen.

        // Binds my model's title to the UITextView's text
        // let _ = self.todo.title.asObservable().bind(to: self.txtTitle.rx.text)
        let _ = self.txtDate.rx.text.map{ strDate in
            return self.dateFormatter.date(from: strDate!)
        }.bind(to: self.todo.dueDate).disposed(by: self.disposeBag)
        let _ = self.txtDescription.rx.text.bind(to: self.todo.description).disposed(by: self.disposeBag)
        let _ = self.switchDone.rx.isOn.bind(to: self.todo.done).disposed(by: self.disposeBag)

        let _ = self.todo.title.asObservable().bind(to: self.labelTitle.rx.text).disposed(by: self.disposeBag)
    }
}
llanfair
  • 1,845
  • 4
  • 27
  • 43

1 Answers1

0

I'd be tempted to have two initialisers: one for when you're creating a new todo item and one for editing an existing one:

let title: Variable<String?>
let description: Variable<String?>
let dueDate: Variable<Date?>
let done: Variable<Bool>

init() {
    self.title = Variable<String?>("Initial title")
    self.description = Variable<String?>("")
    self.dueDate = Variable<Date?>(Date())
    self.done = Variable<Bool>(false)
}

init(todo: TodoModel) {
    self.title = Variable<String?>(todo.title)
    self.description = Variable<String?>(todo.description)
    self.dueDate = Variable<Date?>(todo.dueDate)
    self.done = Variable<Bool>(todo.done)
}

As you've spotted, the infix <-> operator binds the variable to the control property and subscribes to the control property's changes in order to update the variable. It also bundles up their disposables so they have the same lifetime and ensures everything is disposed when the control property's subscription completes. Here's the source:

func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable {
    let bindToUIDisposable = variable.asObservable()
        .bindTo(property)
    let bindToVariable = property
        .subscribe(onNext: { n in
            variable.value = n
        }, onCompleted:  {
            bindToUIDisposable.dispose()
        })

    return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable)
}

If you want to avoid using the <-> operator then you'll need to do all of that for each two-way binding yourself.

Hope that helps.

Paul
  • 1,897
  • 1
  • 14
  • 28