iOS

Design Patterns in Swift #2: Observer and Memento


This tutorial is the second installment in an AppCoda series on design patterns started last week. There are 23 classic software development design patterns probably first identified, collected, and explained all in one place by the “Gang of Four” (“GoF”), Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides in their seminal book, “Design Patterns: Elements of Reusable Object-Oriented Software.”. Today, we’ll focus on two of these patterns, “observer” and “memento,” which fall into what the GoF calls the “behavioral” category.

Software development is an endeavor into modeling real world scenarios in the hopes of creating tools to enhance the human experience in such scenarios. Tools for managing finances, e.g., banking apps and shopping aids like Amazon or eBay’s iOS apps, definitely make life much simpler than it was for consumers just ten years ago. Think of how far we’ve come. While software apps have generally gotten more powerful and simpler to use for consumers, development of said apps has gotten much more complex for developers.

So developers have created an arsenal of best practices to manage complexity, like object-oriented programming, protocol-oriented programming, value semantics, local reasoning, breaking large pieces of code into smaller ones with well-defined interfaces (like with Swift extensions), syntactic sugar, to name some of the most popular. One of the most important best practices that I didn’t mention, but that merits much attention, is the use of design patterns.

Design Patterns

Design patterns are an extremely important tool with which developers can manage complexity. It’s best to conceptualize them as generally templated techniques, each tailored to solving a corresponding, recurring, and readily identifiable problem. Look at them as a list of best practices you would use for coding scenarios that you see over and over again, like how to create objects from a related family of objects without having to understand all the gory implementation details of that family. The whole point of design patterns is that they apply to commonly occurring scenarios. They’re reusable because they’re generalized. A specific example should help.

Design patterns are not specific to some use case like iterating over a Swift array of 11 integers (Int). For example, the GoF defined the iterator pattern to provide a common interface for traversing through all items in some collection without knowing the intricacies (i.e., type) of the collection. A design pattern is not programming language code. It is a set of guidelines or rule of thumb for solving a common software scenario.

Remember that I discussed the “Model-View-ViewModel” or “MVVM” design pattern here on AppCoda — and of course the very well-known “Model-View-Controller” or “MVC” design pattern, long favored by Apple and many iOS developers.

These two patterns are generally applied to entire applications. MVVM and MVC are architectural design patterns and are meant to separate the user interface (UI) from the app’s data and from code for presentation logic, and to separate the app’s data from core data processing and/or business logic. The GoF design patterns are more specific in nature, meant to solve more distinct problems inside an application’s code base. You may use three or seven or even twelve GoF design patterns in one app. Remember my iterator example. Delegation is another great example of a design pattern, though not specifically on the GoF’s list of 23.

While the GoF book has taken on biblical connotations for many developers, it is not without its detractors. We’ll talk about that in the conclusion to this article.

Design pattern categories

The GoF organized their 23 design patterns into 3 categories, “creational,” “structural,” and “behavioral.” This tutorial discusses two patterns in the behavioral category. This pattern’s purpose is imposing safe, common sense, and consistent etiquette, form, and best practices for behaviour onto classes and structs (actors). We want good, consistent, and predictable behavior from all actors inside an entire application. We want good behavior both within actors themselves and between different actors that interact/communicate. Evaluation of actors’ behavior should be considered before and at compile time, which I sometimes call “design time,” and at runtime, when we’ll have lots of instances of classes and structs all doing their own thing and constantly interacting/communicating. Since communication between instances leads to so much software complexity, having rules for consistent, efficient, and safe communications is of paramount importance, but that concept should not in any way diminish the need for good design practices when building each individual actor. Since we’re so focused on behavior, we must remember to use consistent patterns when assigning responsibilities to actors and when assigning responsibilities to combinations of actors.

Before I wax too far theoretical, let me provide tangible examples to assure you that I’ll be demonstrating theory with in-depth practice as realized in Swift code. In this tutorial, we’ll see how to consistently assign responsibility for maintaining an actor’s state. We’ll also see how to consistently assign responsibility for one actor, the subject, to send notifications to many actor-observers, and conversely, how to consistently assign responsibility to the observers for registering with the subject to get notifications.

The whole notion of consistency should be self-evident to you when discussing design patterns. Keep in mind a theme that was highlighted in last week’s post, a theme that will occur over and over as we discuss more and more design patterns: hiding complexity (encapsulation). It is one the highest goals of smart developers. For example, object-oriented (OOP) classes can provide very complex, sophisticated, and powerful functionality without requiring the developer to know anything about the internal workings of those classes. In the same vein, Swift protocol-oriented programming is an extremely important yet relatively new technique for controlling complexity. Managing complexity is a developer’s greatest burden, but here we are talking about taming that beast!

A note on this tutorial’s prose and definitions

For this tutorial, I’ve decided to concentrate my explanatory prose as inline commentary in my sample apps’ code. While I will provide brief explanations of today’s design pattern concepts, I very much want you to look at my code, read the comments, and fully internalize the techniques I’m sharing with you. After all, if you can’t write the code, and can only talk about the code, you’re not going to make it through very many job interviews — and you’re not really a hardcore developer.

You probably noticed in my definition of the behavioral design pattern that I’m following Apple’s documentation convention:

An instance of a class is traditionally known as an object. However, Swift structures and classes are much closer in functionality than in other languages, and much of this chapter describes functionality that applies to instances of either a class or a structure type. Because of this, the more general term instance is used.

I’ve also taken the liberty of referring to classes and structs as “actors” at design time.

The observer design pattern

The observer pattern is probably something you’ve all experienced while using Apple mobile devices, but hopefully you’ve also noticed it while coding your iOS apps. Every time it rains where I live, I and a whole lot of other people around here get push notifications which pop up on our iPhones — lock screen or normal screen — whenever it rains. Here’s what they look like:

PushNotification demo

One source, Apple, is sending out — broadcasting — notifications to many thousands of iPhones, on behalf of the National Weather Service, warning people in my area of the possibility of flooding. In more concrete terms, like at the level of an iOS app, one instance, the subject, notifies (many) other instances, the observers, of changes to the state of the subject. The instances participating in this broadcast type communication don’t need to know about one another. This is a great example of loose coupling.

The subject instance, usually a single critical resource, broadcasts notifications about a change in its state to many observer instances that depend on that resource. Interested observers must subscribe to get notifications.

Thankfully, iOS has a built-in and well-known feature for enabling the observer pattern: NotificationCenter, which I’ll let you study up on yourself here.

Use case for observer design pattern app

My observer example project, available on GitHub, showcases how this broadcast type communication works.

I know this is not quite how the iOS Human Interface Guidelines advise you to do things, but bear with me as I needed an example of a single critical resource. Suppose we were to take a proactive approach and have a single instance of a subject responsible for monitoring network connectivity/reachability. This is the broadcaster, and to implement one, you just need to have an actor adopt my ObservedProtocol.

Suppose there are multiple instances of observers, like an image downloader class, a login instance that verifies user credentials via a REST API, and an in-app browser that all subscribe to the subject to get notified of network connection status. To implement these, you could take pretty much any class and make it a descendent of my “abstract” Observer class, which adopts the ObserverProtocol. (I’ll explain later why I limited my example observer code to a class.)

To implement observers for the purposes of my example app, I created the NetworkConnectionHandler class. When instances of this concrete class get notifications of NetworkConnectionStatus.connected, they turn a UIView instance green; when they get notifications of NetworkConnectionStatus.disconnected, they turn a UIView instance red.

Here’s my sample app code compiled, installed, and running on an iPhone 8 Plus:

Here’s the Xcode console output corresponding to the previous video:

Sample code for observer design pattern app

To see the heavily commented code I just described in the previous section, please look at file Observable.swift in my sample project:

I had intended to put most of the observer’s notification handling logic into an extension of ObserverProtocol but ran into @objc when setting my #selector, then thought about using the block-based version of addObserver(forName:object:queue:using:), then passing in a notification handler closure, blah, blah, blah… I decided that my notification handling code would be much more intelligible and educational as an abstract class.

I also realize that Swift has no formal concept of an abstract class, but you probably already know there is a commonly used workaround for creating one. So, again, to simplify my didactic goal of explaining the observer pattern, I made the Observer class “abstract” by forcing you to override its handleNotification() method. By doing so, I gave you the opportunity to inject any type of specialized logic you want to have executed whenever your Observer subclass instance receives a notification.

Shown immediately below is my sample project’s ViewController.swift file, which shows you how to make use of my core logic in Observable.swift, which we just discussed and reviewed:

The memento design pattern

Most iOS developers are familiar with the memento pattern. Think of iOS facilities for archives and serialization which allow you to “Convert objects and values to and from property list, JSON, and other flat binary representations.” Think of the iOS state preservation and restoration feature, which remembers and then returns “your app to its previous state after it is terminated by the system.”

The memento design pattern is meant to capture, represent, and store the internal state of an instance at a specific point in time and then allow you to find that instance’s state representation at a later time and restore it. When you restore the state of an instance, it should exactly reflect it’s state at the time of capture. While this may sound obvious, you should ensure that all instance property access levels should be respected during capture and restoration, e.g., public data should be restored to public properties and private data should be restored to private properties.

To keep things simple, I used iOS’s UserDefaults as the core of my instance state storage and recovery process.

Use case for memento design pattern app

While I understand that iOS already has facilities for archiving and serialization, I developed sample code that can save and recover an class’s state. My code does a pretty good job of abstracting archiving and de-archiving so that you can store and recover the state of a variety of different instances with varying properties. But my example is not meant for production use. It is a didactic example formulated to explain the memento pattern.

My memento example project, available on GitHub, showcases how a User class instance’s state, with firstName, lastName, and age properties, can be persisted to UserDefaults and then later recovered. At first, no User instance is available to recover, but then I enter one, archive it, and then recover it, as shown here:

MementoDemoApp

Here’s the console output corresponding to the previous video:

Sample code for memento design pattern app

My memento code is straightforward. It provides the Memento protocol and protocol extension for handling and abstracting all the details of gross archiving and de-archiving of the member properties of classes that adopt the Memento protocol. The extension also allows you to print the entire state of an instance to console at any given point in time. I used a Dictionary<String, String> to store an adopting class’s property names as keys and property contents as values. I stored values as String to keep my implementation simple and easily understood, fully well acknowledging that there are many use cases which would require you to archive and de-archive much more complex property types. This is a tutorial about design patterns, not a code base for a production app.

Notice that I added persist() and recover() methods to the Memento protocol, which must be implemented by any class that adopts the Memento protocol. These methods provide developers with the opportunity to archive and de-archive Memento protocol-adopting class’s specific properties, by name. In other words, the elements of the state property of type Dictionary<String, String> can be matched one-to-one with the Memento protocol-adopting class’s properties. Each property name corresponds to a dictionary element key and each property value corresponds to the dictionary element’s value which matches said key. Just look at the code and you’ll understand.

Since the persist() and recover() methods must be implemented by any class that adopts the Memento protocol, properties of all access levels, e.g., public, private, and fileprivate are visible to, and accessible by, these methods.

You may wonder why I made the Memento protocol class-only. I did so because of that horrible Swift compiler message, “Cannot use mutating member on immutable value: ‘self’ is immutable.” Discussing it is way beyond the scope of this tutorial, but if you’d like to torture yourself, read a great description of the issue here.

Here’s my core logic for implementing the memento design pattern, found in file Memento.swift of my sample app:

Here’s the code for implementing the memento design pattern use case I described earlier (archiving and de-archiving an instance of class User), as found in my sample app as file ViewController.swift:

Conclusion

Some critics have claimed that use of design patterns is proof of deficiencies in programming languages and that seeing recurring patterns in code is a bad thing. I disagree. Expecting a language to have a feature for everything is silly and would most likely lead to enormous languages like C++ into becoming even larger and more complex and thus harder to learn, use, and maintain. Recognizing and solving recurring problems is a positive human trait worthy of positive reinforcement. Design patterns are successful examples of learning from history, something humankind has failed to do too many times. Coming up with abstract and standardized solutions to common problems makes those solutions portable and more likely to be distributed.

A combination of a compact language like Swift and an arsenal of best practices, like design patterns, is an ideal and happy medium. Consistent code is generally readable and maintainable code. Remember too that design patterns are ever-evolving as millions of developers are constantly discussing and sharing ideas. By virtue of being linked together over the World Wide Web, this developer discussion has lead to constantly self-regulating collective intelligence.

iOS
Customize Table View Cells for UITableView
iOS
iOS Programming Tutorial: Create a Simple Table View App
iOS
A Look at the WebKit Framework – Part 1
Shares