The Command Design Pattern In Swift

September 16th, 2022

⏱ Reading Time: 15 mins

The purpose of design patterns is to outline solutions to various common software design problems. Becoming popular by the Gang of Four, design patterns provide a theoretical approach on how to solve common challenges that software engineers meet on a daily basis. They do not constitute specific code implementations, therefore they can be adopted and implemented appropriately in many programming languages. Design patterns are separated in three general categories; creational, structural and behavioral. In this post I’m presenting Command, a behavioral design pattern.

The Command design pattern is a behavioral pattern that can be proved pretty useful when writing software. Its purpose is to describe how to encapsulate a request along with any potential parameters as a command to an object in order to trigger an action or event either immediately or at a later time.

Let’s clarify this in simple words using an example. Suppose that we have an app where the user can move a shape towards the four directions (up, down, left, right). This action can be achieved in any of the following ways:

  1. By clicking on-screen buttons that perform each distinct movement.
  2. By using menu commands.
  3. By pressing the arrow keys on keyboard.

The simplest, yet obviously wrong approach, would be to have the same code in three different places. Besides the need to simultaneously maintain or update the multiple instances of the same code, there is an even greater issue here; each part of the app that initiates a movement action is tightly coupled to the actual action performing code. Besides the fact that doing so disregards several software programming principles, it also has one more impact; any change to the action initiator or the action performer would affect the other, possibly leading to undesired behavior and bugs in the app.

Having as a general rule the separation of concerns, the optimum solution would be to have the action performing part independent of the code that starts the action. To achieve that, the Command design pattern provides the necessary guidelines so as to encapsulate a movement request with any additional information into an object as a single command, which will be sent and performed in turn by another object dedicated to this purpose.

There are four entities that participate in the process when following the Command design pattern:

  • Receiver: This is the class or type that implements the mechanisms that actually perform the asked actions when commands are received.
  • Command: A protocol that defines a single method to execute the command. Optionally, a Receiver property can also be declared as well. The Command knows who the Receiver is, as concrete implementations of this protocol contain Receiver instances that perform the actual command execution.
  • Invoker (or Sender): A type that accepts the commands and triggers their execution, without being aware of any of the concrete Command implementations though. The Invoker can also keep a stack of commands, which can be used to trigger undo operations if necessary.
  • Client: It’s the part that initializes instances of all the above types, it assigns commands to Invoker, and generally the place where all other parts are getting coordinated.

We are going to meet all of these through a practical example that follows, details of which are given right next. Even though this post is a bit lengthy, it is worth the effort to go through it and find out how we can put in motion the Command pattern in order to avoid tight coupling between types and perform similar actions initiated in different places.

Demonstrating the Command pattern with a demo app

One of the primary purposes of the Command design pattern was to help build effective user interfaces, removing the tight coupling between visual controls and the code that performs the actual tasks. In that spirit, and in order to explain how the Command design pattern actually works in a practical way, there is a sample project which we’ll be building on throughout this post.

The purpose of that sample project is to bring in life the example that was mentioned previously in the beginning of the tutorial. In particular, the goal here is to manage to move a shape, as well as to change its color, both by using actual buttons and menu items along with their keyboard shortcuts. All that in a SwiftUI-based macOS application.

The following preview will give you the idea:

To follow along properly in the next parts, I’m prompting you to download a starter project, which already contains some boilerplate code. The missing pieces that we’ll add one by one here are those that have to do with the implementation of the Command pattern.

Note: You can also download the complete final project.

Going through the contents of the starter project quickly:

  • In the ContentView.swift you’ll find the implementation of the demo shape, and the layout of all buttons at the bottom side of the window.
  • The ColorButtonsView.swift implements all color buttons at the leading edge of the screen.
  • The MoveButtonsView.swift implements all movement buttons at the trailing edge of the screen.
  • The MoveDirection.swift contains an enumeration with the four possible directions.
  • The ShapeActionReceiver.swift, Command.swift, ShapeActionInvoker.swift are all still empty, but we are going to change that gradually by adding the missing code. Their names make it clear which part of the Command pattern each file regards.
  • The CommandDesignPatternApp.swift file implements the entry point of the app. We’ll add some code to this file, as this is the place where we will initialize instances of both the Invoker and the Receiver parts, as well as we’ll define the menu items.

As you understand, what we really want is similar functionality that will be triggered in two (three indirectly) different places; the on-screen buttons and the menu commands. But we need this without repeating the core code, and the Command pattern will help us on that. Let’s go for it!

Implementing the Receiver

Open the ShapeActionReceiver.swift file, as this is where we’ll implement the Receiver part. The homonymously named type is going to be containing properties that will be used by the SwiftUI views and will be defining the shape’s position and appearance.

However, in order to make this possible and have the views get updated properly, the ShapeActionReceiver that we are implementing next must be conforming to ObservableObject protocol. Just for that it has to be a class:

ShapeActionReceiver will have three stored properties, all of which should be marked with the @Published keyword so changes to them get reflected in SwiftUI views. The two of them will be containing the shape offset in the horizontal and vertical axises. The third one will be storing the current color of the shape:

Note: None of the above properties’ values is allowed to be changed when accessed out of the instance that they live in. We specify them as read-only with the private(set) prefix, and there is a reason for that; we want them to be updated only through the methods that we’ll implement next.

The ShapeActionReceiver class will be performing two distinct actions; it will be updating the offset properties so the shape can be moved towards the proper direction, and the color property with a different desired color.

With that in mind, here’s the first method that updates the offsetX and offsetY properties; note that:

  • The desired direction is given as argument
  • We change each offset by a predefined number of points.

The second method simply accepts a Color object and stores it to the color property:

That concludes the simple implementation of the Receiver part in this demonstration. Here’s the entire implementation:

The Command protocol

Next up is the Command part, which is represented by a protocol programmatically. As said in the beginning, it defines a single method (usually called execute()) that gets implemented by concrete Command types and triggers specific actions. If there is a single Receiver type like in this tutorial, Command protocol can optionally also contain a property of the Receiver type, so the concrete types mandatorily implement them.

Note: A Receiver property should exist mandatorily in the concrete implementations; by containing it in the Command protocol we simply make it a requirement.

We’ll see more about concrete implementations in the part that follows next; for now let’s focus on the Command protocol in the Command.swift file:

Notice the AnyObject conformance in the above code. Even though this is not part of the ordinary implementation, it becomes necessary here; the ShapeActionReceiver type is mandatorily a class as it adopts the ObservableObject protocol. In extension, we are required to make explicit that Command can by adopted by classes only since it contains a ShapeActionReceiver type. And AnyObject does exactly that.

Concrete Command implementations

There are two types of actions that the Receiver type can initiate; the movement of shape by changing the offset values, and the change of shape’s fill color. Updates to the respective properties will force the user interface to be updated accordingly.

We can definitely say that the two actions match to two different commands. Because of that, we are going to implement two different concrete Command types, and both of them will adopt the Command protocol. We’ll name the first one MoveCommand, and since Command must be adopted by classes only, it’s going to be a class:

MoveCommand will contain two stored properties. The first one is the shapeActionReceiver required by the Command protocol. The other is going to be storing the desired direction towards which the displayed shape should be moved. None of these properties will have initial values; they’ll be given to them through the initializer that we’ll also add for this purpose:

There is one more thing to do; to implement the required execute() method that will initiate the actual action. It will do so by calling the move(direction:) method through the shapeActionReceiver object:

With that, the first concrete Command implementation is ready:

We are going to create the second Command type in an exact similar manner. We’ll call this one PaintCommand, with the differences being the kind of the second property (a Color object in this case instead of the direction), the initializer, and the body of the execute() method:

The Command part is now complete as well, so time to focus on the third player in the game, the Invoker.

Implementing the Invoker

The Invoker as its name suggests invokes, or in other words, sends the message to execute a wanted command. That’s why it’s also called the Sender. The main and constant purpose of such a type is to accept commands and execute them. However, it can be implemented so as to satisfy some additional needs. For instance, besides than only containing methods that execute commands, it could also provide the possibility to store the commands it receives over time and create a history stack.

Just because of the latter, the Invoker can also help in implementing undoable operations; operations that can be undone or redone using with the traditional Undo/Redo actions provided by the system, as well as custom Undo mechanisms.

Having said that, the Invoker in the current example is going to be a class that will be implementing a single method. This method will be getting a Command instance as argument, which will be used to access and invoke its execute() method that starts the matching action.

In the ShapeActionInvoker.swift file let’s add the following implementation:

Notice a particularity specific to this demo project; generally, the Invoker does not have to be conforming to the ObservableObject protocol. However, this is necessary here for the following reason:

We are going to add an instance of the ShapeActionInvoker type to the SwiftUI environment in the next part. But doing so must satisfy the next requirement; the type of any instance that is added to the SwiftUI environment must be adopting the ObservableObject protocol. Subsequently, we can’t do anything other than adopting it as shown in the above code.

Besides that, you will notice that the Invoker does not know anything about any concrete Command type; all it’s aware of is that it accepts an instance of a Command type and invokes its execute() method.

The above Invoker implementation is all we need in this tutorial. Time to find out how to use everything in the SwiftUI app, which is the Client part of the Command design pattern here.

Using the Client to put all the above in motion

With all the above in place, let’s switch to the CommandDesignPatternApp.swift file. That’s the entry point of the app, and the place where the ContentView (the root SwiftUI view in this project) is initialized and added to the window. We will declare and initialize at the same time in this file the following two stored properties:

The reason these two properties are added here is simple; we’ll pass both instances to the SwiftUI environment, so they are available to be accessed by all views in the hierarchy. But on top of that, we will also use them in this file in order to enable shape movement and color change through menu commands and keyboard shortcuts.

For now let’s start with the former:

We will return in this file in a while again, but for now let’s focus on adding all the missing pieces to the SwiftUI views. We will begin by updating the ContentView.swift file, where the first step is to access the shapeActionReceiver property in the environment like so:

There are two changes that we’ll perform:

  • We’ll replace the default blue color of the shape with the color specified in the shapeActionReceiver object.
  • We’ll replace the zero offset values with those also specified in the shapeActionReceiver.

By doing the above, every time any of those values gets a new value, the SwiftUI will re-render its contents and reflect the changes made on the shape visually. Here is how the arguments provided to the view modifiers of the RoundedRectangle() shape should look like:

Enabling the movement buttons

Next stop is the MoveButtonsView.swift file. The buttons to move the shape are implemented in this view, however none of them contains any action to perform at the moment. That’s something we’ll fix now, but similarly as before, the first step is to access the objects we added to the SwiftUI environment first:

Let’s work on the button that moves the shape up first. There is no doubt that we could forget about the Command and the Invoker parts and move the shape by calling the move(_:) method of the shapeActionReceiver object directly:

Doing so, however, is wrong and instantly erases all the previous implementation that has been done so far. Even though this will work, it does not lift the biggest problem that the Command design pattern solves; the tight coupling between the view and the object that performs the action.

Instead of that, we will initialize a MoveCommand initially providing the desired direction, and then we’ll supply it as argument to the updateShape(using:) method of the shapeActionInvoker object:

Notice here that the Client, the SwiftUI view in particular, is the one who initializes the appropriate Command object, which passes to the Invoker in turn. And even though it knows the exact type of the Command instance as it creates it, the Invoker object that accepts it does not care of that exact type as we’ve already seen.

The above could be written in a shorter way like so:

In a similar way we’ll update the rest of the buttons, with the only difference being the value of the direction:

Enabling the color buttons

As you can see in the above code snippets, all we have to do is to create a concrete Command instance and pass it to the Invoker right where we want a command to be executed. All that, of course, once the preparation of the main parts of the Command design pattern has been already done. We will keep working on that spirit in order to add the missing actions to the buttons that change the shape’s fill color.

For that, we’ll go to the ColorButtonsView.swift file, and exactly as we did before, we will declare two @EnvironmentObject properties in the ColorButtonsView so as we can access the Receiver and Invoker:

With the above two being handy, we can implement each button’s action. Pretty similarly to the previous buttons, we’ll be initializing a Command, in particular a PaintCommand instance this time, and we’ll be supplying it as argument to the updateShape(using:) method of the shapeActionInvoker object. In fact, besides the command itself, there’s nothing else that gets changed compared to what we already did above. So, here’s the implementation of all buttons at once:

It’s a good time now to run the app if you want so; depending on the clicked button, you’ll see the shape updating its position in the horizontal and vertical axises, as well as to change its fill color.

However, even though it works fine using the buttons inside the app’s window, there are still no menu commands and keyboard equivalents to do so. Let’s make that happen next, repeating quite easily the above functionality thanks to the implemented Command pattern in our project.

Executing shape commands using menus and keyboard shortcuts

Time to add the final touches to the demo project. Back to the CommandDesignPatternApp.swift file, we’re going to apply the command(_:) view modifier to the WindowGroup container in order to add two new menus along with their items. To keep things simple, we won’t bother removing the other menus added by default when creating a macOS app in Xcode.

So, for starters add the following:

If you run the app, you will see that there are two new menus in the top bar:

However, clicking on any of them has no result, as they still lack of menu items. Usually, actionable items are SwiftUI buttons, where each button can be optionally assigned with a keyboard shortcut equivalent. Focusing on the Move menu items initially, we need four buttons for moving the shape to the four different directions. You can see these buttons in the snippet that follows, as well as their matching keyboard shortcuts; we specify shortcuts by applying the keyboardShortcut(key:modifiers:) modifier on each button:

The base where we’ll step on is now ready regarding the Move menu. All we are still missing is the actual action that each button will be performing. But guess what; what we have to do here is something that we have already done earlier!

In particular, in the action closure of each button we’ll initialize a MoveCommand instance, which we’ll provide as argument to the updateShape(using:) method of the shapeActionInvoker object. Being able to do so right here is another reason why we declared the Receiver and Invoker properties in the CommandDesignPatternApp struct previously:

With just this small addition the Move menu is going to work just fine now. The respective menu items can be used to move the shape up, down, left or right, and on top of them, we can also use the arrow keys in the keyboard.

Note: The second argument in the keyboardShortcut(key:modifiers) modifier is an empty array so as to indicate that we don’t want to simultaneously press any modifier keys (like Command or Shift) along with the arrow keys. Avoiding to do so, system will set the Command modifier by default.

As you may guess, adding the menu items to the Paint menu hides no surprises, as what we have to do is pretty much the same to what we did right above. In fact it’s going to be simpler; we won’t specify any keyboard shortcuts to update the shape’s color:

The Paint menu is working now too, and you can find this out by running the app once again. With this last code, the Client part of the Command design pattern is complete for the demo project in this tutorial, so consider the job done!

Conclusion

As you have found out through the implementation of the previous parts, the great benefit of the Command design pattern is that it removes the tight coupling between the part of the app that initiates an action, and the part that actually performs it. In addition, it makes it pretty easy to execute the same commands in more than one places, still keeping the various parts independent of each other.

One question that might come in your mind is why do we really need to use the Invoker in the Client in order to execute commands, given that the latter is the one that initializes all command objects that encapsulate the actual requests. Just think of this as an answer; what if besides simply initiating the commands’ execution we would also need to keep track of them so we are able to perform undo operations? Or to perform other book-keeping tasks regarding the commands? With the Invoker we are able to implement such capabilities in one place only, and exactly right when they should be. Without it we would need to repeat that code in each occurrence where the Client initializes commands, and start messing things up again. Besides, let’s don’t also forget that the Invoker is part of the Command pattern, so if we skip it, then we simply do not really follow the pattern.

With that said, it’s time to reach to the closing of this tutorial. As a last word, even though it might seem initially that we have to write additional code to implement the Command pattern, the truth is totally different. By adding a few more types that usually don’t take long to define, we get multiple gains; we avoid code repetition, make reusability easier, testing and code maintenance much simpler, and separation of concerns clearer. So, why not to get into the trouble to adopt the Command design pattern in our projects if it fits?

Thank you for reading! ????


????‍???? The complete project is available for download.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.