Welcome to a series of posts dedicated to learning about design patterns. Whilst a lot of the ideas are code-agnostic, we’re aiming to show you how to implement them in Swift (Swift 3.0 as of the time of writing). Each post is independent of each other and all code for the projects will be available here on Git.
Okay, so for todays example, picture your an iPhone, specifically the Springboard on the iPhone. You have a grid of apps that a user can tap at any point and execute. You also have a home button which closes your apps (okay, it doesn't, it pushes them to the background, but stay with us). Each app runs its own specific list of commands when it runs and when it closes.
At the moment, when you — the iPhone — look at the Weather and Clock app, you see these functions exposed:
How do you start an app? Well I guess you would call getWeatherData() first on the WeatherApp, and then maybe showWeatherAnimation(). On the ClockApp, you would just have to call showClock(). A bit confusing for the poor iPhone. Two completely different objects, with no real way of knowing what to do without having prior knowledge of how the objects work.
What the iPhone needs to be able to do, is to encapsulate those commands into another function so that it knows how to launch the app, without having to have into intricate knowledge on how the app works. This is solvable with the Command pattern. (This example was really a stretch. The iPhone will use a much more complicated system is real life, but it provides a nice visual idea that we can all picture in our heads to demonstrate how the pattern works!)
What the Command pattern allows us to do is to turn our Commands into objects that will execute the correct list of functions depending on the object, without having to modify the object itself (it would be a pain if we had to ask the developers of these two applications to change their codebase). For instance, we would create a class called WeatherAppOpen that would conform to a Command protocol, and would execute getWeatherData() and showWeatherAnimation() when a generic execute() function is called.
This is what the Command pattern for the iPhone’s Springboard looks like in UML form:
The Springboard never communicates directly with the WeatherApp because the necessary commands are abstracted to a common generic method, meaning the Springboard only needs to be aware of one method.
How that looks in code
To start, create a new project in Xcode. We won’t be touching anything in UIKit so create a macOS Terminal project.
We’ll create the Command protocol first that all of our command classes will conform to. Its relatively simple, and just contains the execute() function that the Springboard will talk to.
Next, we’ll create our WeatherApp and ClockApp classes. These classes will just contain some boilerplate methods that print out to the console so we could see what our theoretical Springboard would be doing. They will look like this:
We now need to access those methods via the Command protocol. To do this, we create the object as the type of Command we wish to complete I.E open or close the app. Starting with opening the Weather app, we’ll create WeatherAppOpenCommand.
We invoke the class by passing through an instance of the Weather App (if this was real life, that would probably be a Singleton in the case of the iPhone — post coming soon!). When execute() is called, we then launch the app, using the specific commands to the Weather App. We do the same with closing the Weather App, which looks like this:
Starting to get the picture? We can then build the Clock App commands in exactly the same way, but call the methods specific to the Clock App:
We can now look at building our pretend (and very simplistic) Springboard.
The Springboard needs to be able to hold an array of open and close commands (our apps). It then needs to be able to execute a command at specific index if an app is available. It will look like so:
The setCommand function allows us to effetely place an app at an index on the Springboard, and the open/closeApp functions allow us to execute the correct command at the index provided.
So now we’ll build the client (or the iPhone in this case).
We create an instance of the Springboard, an instance of the Weather App, and an instance of the Clock App. We set the commands using the Open/Close objects relative to each app, and pass through the instance of the app to the relevant commands.
Run the program, and you’ll get an output that looks like this:
Getting weather dataShowing correct weather animationDeleting weather dataClosing weather appShowing clockRemoving clock
Hurray you built an iPhone*! You’re running the apps, in an expandable, encapsulated way, that the Springboard knows nothing about!
Sam Stone (@Stonesam92) | Twitter
The latest Tweets from Sam Stone (@Stonesam92). Freelance Mobile Developer and Hip-Hop fan. Based in Manchester.…
*we did not build an iPhone