Skip to content

Instantly share code, notes, and snippets.

@jspahrsummers
Last active May 2, 2019 11:56
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jspahrsummers/7080228 to your computer and use it in GitHub Desktop.
Save jspahrsummers/7080228 to your computer and use it in GitHub Desktop.
Advantages and disadvantages to RACCommand under MVVM

MVVM example

For a command that initiates a network request when:

  • The network connection is up (precondition)
  • The network request is not already in progress (enforced serialization)

The lifecycle proceeds as follows:

  1. The view model exposes the command and describes its behavior
  2. The view binds the command to a control
    • The control will automatically be enabled when the command's preconditions (above) pass
  3. When the control is clicked, the command is executed
  4. When results arrive from the network, the view model handles them
  5. The view model eventually fires some property or notification to update the view

Setup:

VM (creates command) ---> (passed to) View ---> (passed to) Control

Execution:

Control ---> Command ---> VM ---> View

Benefits

  • The view doesn't need to know when the command is valid, since the control is automatically enabled/disabled via its rac_command extension
  • The command can be bound to multiple input sources in exactly the same way
  • The command can be directly executed while preserving all of its guarantees
  • The view doesn't need to know anything about the behavior that will occur, since it does not directly receive the results of the command
  • The view can present errors (via RACCommand.errors) without caring why or how they occurred
  • Signals are automatically delivered to the main thread, which helps avoid bugs in UI code

Drawbacks

  • Receiving results is confusing, because signals of signals (like executionSignals) are confusing
  • Views have access to more information about the command than they need, like executionSignals, allowsConcurrentExecution, etc.
  • The relationship between the signalBlock and execution is unclear
  • The view model should respond to the view out-of-band (this is simpler, but occasionally makes things harder as well)
@jspahrsummers
Copy link
Author

Great points, all. Thanks!

@anaisbetts
Copy link

Maybe a more useful name for them in RAC would be "Actions", so that it would seem related to "Target/Action Framework". It's interesting that the Command isn't obvious to Cocoa devs, which is why I wonder if a better name would evoke a quicker understanding.

@lawrencelomax
Copy link

My biggest reason from staying away from RACCommand has been the ability to pend subscription to the execution signals, rather than erroring if the command is disabled.

For example, I have a video player that will occasionally misbehave if it is sent play and pause commands when it is in an invalid state from the Application suspending then resuming again. I can have a signal that represents the enabledness of any player action such as a play action, defined in terms of the player being in an invalid state.

RACCommand will not defer subscription to the execution signal if it is disabled. I want to enqueue a play as soon as the app resumes, but not have play called on the player itself until it becomes enabled again. The implementation is pretty simple, setting an enqueue state & enabledness on properties, then using RACObserve and combineLatest: to send a value when the a 'command' is enqueued and enabled. I'm using this instead of flatten, switch and its friends because enqueuing a pause should cancel any previously enqueued play, and I'd like to subscribe to each of the player commands individually.

This may not be the problem that RACCommand is trying to solve and may not be relevant to the MVVM use case.

@alanjrogers
Copy link

@jspahrsummers I don't have anything to add, but I'm 👍 on those pros and cons.

@lawrencelomax: Not entirely sure what you're talking about, could you post a code sample somewhere?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment