Chapter 24 - Building a Complete RxSwift App - EditTaskViewModel

Hello,

I’m learning RxSwift and I’m trying to make as many changes as I can just for learning to work with this awesome library. I added a property to TaskItem called type as a string. I create everything to show this new property on QuickTodo application, the problem is don’t know how to save the new data when the user taps Create item. For now I’m saving the title on the type property. I have the following code:

class TaskItem: Object {
  @objc dynamic var uid: Int = 0
  **@objc dynamic var title: String = ""**
  @objc dynamic var type: String = ""

  @objc dynamic var added: Date = Date()
  @objc dynamic var checked: Date? = nil

  override class func primaryKey() -> String? {
    return "uid"
  }
}

struct TasksViewModel {
  let sceneCoordinator: SceneCoordinatorType
  let taskService: TaskServiceType

func onUpdate(task: TaskItem) -> Action<String, Void> {
    return Action { **newTitle** in
      return self.taskService.update(task: task, title: newTitle, **type: newTitle**).map { _ in }
    }
  }

struct EditTaskViewModel {

  let itemTitle: String
  **let itemType: String**
  let onUpdate: Action<String, Void>
  let onCancel: CocoaAction!
  let disposeBag = DisposeBag()
...

And in the EditTaskViewController class I have a Text Field called typeTextField.

class EditTaskViewController: UIViewController, BindableType {

  @IBOutlet var titleView: UITextView!
  **@IBOutlet weak var typeTextField: UITextField!**
  @IBOutlet var okButton: UIBarButtonItem!
  @IBOutlet var cancelButton: UIBarButtonItem!

  var viewModel: EditTaskViewModel!

  func bindViewModel() {
    titleView.text = viewModel.itemTitle
    typeTextField.text = viewModel.itemType

    cancelButton.rx.action = viewModel.onCancel

    okButton.rx.tap
      .withLatestFrom(titleView.rx.text.orEmpty)
      .subscribe(viewModel.onUpdate.inputs)
      .disposed(by: self.rx.disposeBag)
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    titleView.becomeFirstResponder()
  }
}

What I don’t know how send more data when the user taps the Ok button in the following code:

okButton.rx.tap
      .withLatestFrom(titleView.rx.text.orEmpty)
      .subscribe(viewModel.onUpdate.inputs)
      .disposed(by: self.rx.disposeBag)

How to tell the onUpdate Action that receive the titleView and typeTextField data?

I did it updating the TaskItem directly in the EditTaskViewController, but I don’t want to do that, I want the controller doesn’t know anything about the model, I want to send data back through viewModel object.

Thank you very much.

@fpillet Can you please help with this when you get a chance? Thank you - much appreciated! :]

Hi @williammr,

You want to pass both strings when the user presses the OK button. For this, one solution is to modify the signature of the onUpdate action to accept a tuple of strings:

func onUpdate(task: TaskItem) -> Action<(String, String), Void> {
    return Action { fields in
      return self.taskService.update(task: task, title: fields.0, type: fields.1).map { _ in }
    }
  }

Now you want to combine the latest value of both fields and pass it on as input to your action:

// this creates an Observable<(String,String)>
let combinedFields = Observable.combineLatest(
      titleView.rx.text.orEmpty,
      typeView.rx.text.orEmpty)

okButton.rx.tap
      .withLatestFrom(combinedFields)
      .subscribe(viewModel.onUpdate.inputs)
      .disposed(by: self.rx.disposeBag)

There are other ways to do this (you could make an ad-hoc structure to contain the updated information and pass it on to the action, it makes future changes easier to insert), I’ll leave it as an exercise to you :slight_smile:

Hope this helps,
Florent

Thank you very much. It worked.

I found that I have to study the RxSwift book again. My problem is that I don’t understand how to create the Action signature (as many other things!!!), I did try with:

Action<[String], Void>

Actually on the book there is an example:

// Composing behavior 
let loginPasswordObservable = Observable.combineLatest(loginField.rx.text, passwordField.rx.txt) {
  ($0, $1)
}

loginButton
  .withLatestFrom(loginPasswordObservable)
  ...

Do you have some Action signatures examples? or in another version of the book, you probably going a little bit deeper with Action.

And I am going to try with an ad-hoc structure as you suggested.

Thank very much.
William

This topic was automatically closed after 166 days. New replies are no longer allowed.