Navigation Menu

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)
@tonyarnold
Copy link

I have to be honest, a lot of RACCommand goes straight over my head. I find it rather difficult to implement properly, and while I'm sure it's just a matter of more learning on my part, I feel like it should be easier to work with. Given my questionable understanding of the topic, I'm not sure I have anything that helps here. 🐒

@erikprice
Copy link

Not sure if it's something you want to call out here, but there's also the behavior of commands with respect to subscribing and delivering on the main thread scheduler. Some clients may have come to expect this.

@jdewind
Copy link

jdewind commented Oct 21, 2013

I agree with benefits and drawbacks that you have listed above.

If I had to emphasize a particular set of drawbacks it would be the first and second items you listed.

  • I found myself initially trying to use executionSignals as the result signal.
  • If there isn't a rac_command abstraction I lean towards using the executing signal on the view and avoid exposing the command.

Improvements to note that I liked when upgrading to 2.0:

  • executing signal instead of a "solid" boolean value
  • errors signal

@ashfurrow
Copy link

I think you hit the nail on the head with the first drawback: it's confusing. What a command is and how it is used is not obvious, even in the context of ReactiveCocoa.

That said, I'm going to use it when I implement MVVM.

@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