iOS

Design Patterns in Swift #1: Factory Method and Singleton


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.” This tutorial focuses on two of those patterns in terms of what the GoF calls the “creational” category: factory method and singleton.

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 specific 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 creational category. This pattern’s purpose is to make the creation of (often complex) objects straightforward (easy), understandable, and maintainable for developers, hiding details like instantiation and class implementation.

Hiding complexity (encapsulation) 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 creational pattern, a developer may not even have to know about a class’s key properties and methods, but if necessary, she/he can take a peek at the interface — protocol in Swift — to the class(es) of interest and just plug and play. You’ll see what I mean in our first example of a “factory method” design pattern.

The factory method design pattern

If you’ve delved into the GoF design patterns and/or spent a lot of time in the world of OOP, you’ve probably at least heard of the “abstract factory,” “factory,” or “factory method” pattern. While we could quibble over “exact” nomenclature, the example I’m going to show you here most closely fits the “factory method” pattern.

In this paradigm, you can create very useful objects without directly calling class constructors and without knowing anything about the class(es) or class hierarchy that your factory method is instantiating. You get a lot of bang for cheap. You get functionality and UI (if applicable) with a minimal amount of code. So my factory method example project, available on GitHub, showcases how objects from a nontrivial class hierarchy can be used easily by, say, a team’s UI developer:

Most successful apps have a consistent look — a theme — that is pleasing and becomes associated with the app and/or app developer. We’ll assume that all shapes used in our hypothetical app will be the same color and same size so as to stay in tune with the app’s theme — it’s branding. These shapes could be useful through an app as custom buttons, or maybe just part of background imagery during the onboarding process.

Let’s assume that the design team has agreed that my app theming code has been selected to be used as app background imagery. We’ll go through my code, starting with the protocol, class hierarchy, and factory methods that our hypothetical UI developer shouldn’t have to worry about.

See file ShapeFactory.swift. Here’s a protocol for drawing shapes inside preexisting view controllers. Since it could be used for a variety of purposes, it’s access level is public:

Remember that the UIView class has a rectangular frame by default, so it was simplest for me to make Square my base shape class:

Notice that I’m taking advantage of OOP to reuse my code and thus make my shape hierarchy simple and maintainable. Classes Circle and Rectangle are just specializations of Square (and remember how easy it is to draw a circle by starting with a perfect square):

I’ve used fileprivate to emphasize one of the purposes behind the factory method pattern: hiding complexity. You should also see how the shape class hierarchy could be easily modified or extended without changing the factory methods below. Here’s the code for the factory methods that makes object creation so abstract and simple:

Notice that I’ve developed a class factory and two factory methods to give you some food for thought. Technically, a factory method should return one of a number of related classes, all with common base class and/or a common protocol. Since the purpose here was to draw a shape in a view, I personally prefer the createShape(_:view:) method. Sometimes it’s a good idea to provide alternatives — or just to experiment and explore new possibilities.

Finally, I show the use of two factory methods to draw shapes. The UI developer doesn’t have to know anything about how the shape classes are encoded. He/she especially doesn’t have to worry about how shape classes are initialized. The code in file ViewController.swift is easily readable:

The singleton design pattern

Most iOS developers are familiar with the singleton pattern. Think of the UNUserNotificationCenter.current(), UIApplication.shared, or FileManager.default singletons that you have to use if you want to send notifications, or open a URL in Safari, or manipulate iOS files, respectively. Singletons can be good for protecting shared resources, providing access to some system which contains one and only one instance of an object, supporting one object which performs some type of app-wide coordination, and, as we’ll see here, can be good for providing a value-added wrapper of a built-in iOS singleton.

To act as a singleton, we make sure that a class:

  • declares and initializes a static and constant property of itself, and calls that property shared to convey the fact that an instance of the class is a singleton (public by default);

  • declares a private property of some resource we want to control/protect but also share via shared; and,

  • declares a private initializer so that only our singleton class can initialize itself, and inside this init, we initialize the resource we want to control but also share.

By making the class’s initializer private and defining the shared static constant, we’ve ensured that there can only be one instance of the class, the class can only initialize itself once, and the class’s shared instance is accessible throughout our app code. We’ve created a… singleton!

My singleton example project, available on GitHub, showcases how a development team can store user preferences safely, consistently, and with very few errors. Here’s my sample app remembering whether the user prefers to see their password as unencrypted plain text or as masked, which is not the greatest idea in retrospect, but I needed an example to show you that my code works. This code is solely for educational purposes. I advise you never to leave a password exposed. Here’s how the user can set her/his password preference — and that preference is saved in UserDefaults:

Show_Pwd

When the user closes and eventually comes back to the app, note that her/his password preference is remembered:

Let me show you an excerpt of the code in file PreferencesSingleton.swift, with inline commentary, and you’ll see exactly what I mean:

There’s no need to worry about impact on, say, app startup as initializers of static properties and global variables are run lazily, as I understand Swift.

You may ask, “Why has he created a singleton wrapper of another singleton, UserDefaults?” First, my main purpose herein was to show you a best practice for creating and using a singleton in Swift, and user preferences is the type of resource that should have a single point of entry. So UserDefaults served a very obvious didactic example. But secondly, think about how many times you’ve seen UserDefaults used (abused) almost flippantly through an app’s code base.

I’ve seen apps where UserDefaults (or NSUserDefaults in the “old” days) were spread all over project code without any rhyme or reason. Every single reference to a key in user preferences was spelling out by hand. I just found a bug in my own code where I had misspelled the word “switch” as “swithc,” and because I had copied and pasted, I had ended up with quite a few instances of “swithc” before I caught the problem. What if other team member(s) on that app started or continued to used “switch” as a key in saving the corresponding value? The app could’ve ended up with two or more states being preserved for what should’ve been one state. UserDefaults uses strings as the keys to the values we wish to maintain as part of app state, which is fine because it’s best to describe something — values — with meaningful, easily identified, and easily remembered words. But strings are not without risk.

Many of you have probably read about what has become known as “stringly-typed” code, as in my discussion about “swithc” vs. “switch.” While strings are very descriptive — a good thing — the use of raw strings as unique identifiers all over a code base is likely to lead to subtle yet eventually catastrophic errors because of misspellings. The Swift compiler doesn’t prevent us from making stringly-typed mistakes.

The solution to stringly-typed errors is making use of string constants in the form of the Swift enum. Not only can we standardize our use of strings, but we can organize them by breaking them into categories. Again, see PreferencesSingleton.swift:

I’m starting to wander from the definition of the singleton design pattern, but I do want to briefly show you and explain why I myself use a singleton wrapper for UserDefaults in most of my production apps. There are many value-added features that can make a UserDefaults singleton wrapper very convenient and enhance the reliability of code. Providing error checking when getting and setting preferences immediately comes to mind. Another feature I like to add is providing convenience methods for commonly used user preferences, like for how to handle passwords. You’ll see below as you read my code. Here’s everything in my PreferencesSingleton.swift file:

If you look at my ViewController.swift file, you’ll see how easy it is to access and make use of a well-constructed singleton:

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.

macOS
Learn macOS Development: Develop a Music App with Audio Playback and File Upload Features
iOS
An Introduction to SpriteKit Part 2: Actions, Sequences and Gesture Recognizers
iOS
An Introduction to AR Quick Look in iOS 12
Shares