Intro to object-oriented principles in Swift 3 via a message box class hierarchy

[Download Xcode 8.2.1 project with full Swift 3 source from GitHub.]

Let’s talk about using Swift 3’s object-oriented programming (OOP) features to make you a better developer. First, we’ll get a quick overview of the sample project for this post. Second, we’ll discuss the advantages of OOP in Swift. Third, we’ll talk about OOP in depth. Fourth, let’s think about how all the OOP theory applies to my code. Fifth, we’ll specify the Swift 3 syntax required for defining classes and creating instances (objects) of those classes. Finally, we’ll go through my Swift source code to implement a useful OOP class hierarchy (which you are free to include in your own projects subject to my terms of usage). Hey! Check out my latest post on polymorphism, a natural continuation of this article.


Sample Swift OOP project overview

I used Swift 3 to create a message box class hierarchy to demonstrate object-oriented principles like classes, inheritance, encapsulation, reusability, and polymorphism. Here’s a video showing my message box class instances (objects) in use, starting at the top of the screen with the most general class, progressing downwards to more specialized descendent classes. This is inheritance and other object-oriented principles in action:

Here’s an instance of the base class, the simplest form of an alert, with a title, message/description, and button with default functionality to dismiss the alert (click to enlarge):

Figure 1. IBMessageBoxDismiss, the base message box class.

Here’s an instance of the first descendant of the base class, an alert with a title, message/description, and two buttons, the leftmost with the default dismiss/cancel functionality, and the rightmost with an “OK” button which can express some functionality you specify (click to enlarge):

Figure 2. The IBMessageBoxDismissAndOK class, the first descendant of the base class.

Here’s an instance of the third descendant of the base class, an alert with a title, message/description, a text box, and two buttons, the leftmost with the default dismiss/cancel functionality, and the rightmost with an “OK” button which can express some functionality you specify. The text typed into the text box can be accessed by the closure associated with the rightmost button — or can be accessed anytime after the alert is dismissed (click to enlarge):

Figure 3. The IBMessageBoxDismissAndOKWithTextField class, the third descendant in the class hierarchy.

Many iOS developers create UIAlertControllers on an as-needed basis to display alert messages to the user. This can lead to an excessive amount of code, especially in complex and highly interactive apps. Most developers were trained to consolidate code that is needed frequently and in many different states into a class or method/function, but it seems that alerts are often ignored when it comes to writing maintainable code. This may be due to the fact that, according to Apple, “The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.” But that certainly doesn’t mean you can’t encapsulate (or “wrap”) a UIAlertController in your own “alert” or “message box” class. I keep the code in this project handy and use it in most of my app work so that displaying an alert, especially one that triggers a complex process, just takes a few lines of code. Remember that you can associate a block with an alert’s button (the “handler,” which is “A block to execute when the user selects the action”).

Advantages of Swift 3’s object-oriented features

Reusability (with inheritance)
Developers can create pieces of code (classes) that can be readily reused later. Code developed in one project can be used in the next project, or many other projects. If you’re not familiar with OOP, but have done some iOS programming, you’ve probably already used Swift OOP. Whenever you create an new iOS project using the “Single View Application” template, Xcode automatically creates a subclass of UIViewController called “ViewController.” There are hundreds of thousands of apps that have already reused classes like UIViewController, saving developers from reinventing the wheel every time they start a new project.

If a developer already has created a great class, another developer can create a new descendant of that original class and specialize it. The original (parent) class doesn’t get damaged and maintains its usefulness, the new (child) class inherits all the features of the parent, and the new class can extend the parent in possibly marvelous ways. Later, a grandchild of the child class can be created and the cycle of reusability through inheritance can continue forever.

Maintainability
Because classes are? single well-defined models of real world objects or concepts with succinct interfaces, they’re easier to debug, fix, modify (and extend as we discussed in the section “Reusability (with inheritance)”). Notice that I highlighted the word “are” in the previous sentence and followed the word with a question mark. Classes should be single well-defined models of real world objects or concepts with succinct interfaces, but it’s up to you to design them properly. You need to design your classes with several core concepts in mind: model your classes after the target product, keep classes simple and short, build classes with a single or few purposes, and keep class interfaces small/simple.

If you’re tackling a large problem, remember that complex systems are objects that can be broken into smaller objects. Think of the human body. If you were modelling it in code for a medical application, wouldn’t you start with a top-level class like “humanBody?” Wouldn’t you then break the body into high-level classes like neurologicalSystem, circulatorySystem, and skeletalSystem? Wouldn’t you then break those objects into separate class hierarchies descending from neurologicalSystem, circulatorySystem, and skeletalSystem with classes grouped as {brain, nerves, neurons}, {heart, veins, arteries}, and {bones, bones marrow}, respectively?

When you express classes as objects in code — make use of your classes as objects/instances — they’re much easier to organize, find, and use. Think of heart.beat(), neuron.fire(), or bone.getDensity(). When you set a breakpoint and stop on any object reference in the Xcode LLDB (debugger), you can see all the data associated with that object of interest. When classes encapsulate complexity, they’re manageable.

Hiding complexity (encapsulation)
OOP classes can provide very complex, sophisticated, and powerful functionality without requiring the developer to know anything about the internal workings of that class. As long as the programmer understands the key properties and methods of the class, visible via the class interface, he or she can just plug and play. Think about your usage of the UIViewController class — or at least start learning about it by clicking the link and reading about it.

Abstraction and polymorphism
Do some research on the term “taxonomy,” defined as the “orderly classification of plants and animals according to their presumed natural relationships.” The same applies to classes in software development infrastructures like iOS. You have high-level generalized classes like UIView and UIControl, and then you have their descendants getting more and more specialized for specific uses.

A UIView can be a general container that you use to group/organize your user interface. Note that a UIImageView is a descendant of a UIView, but is specialized for displaying images, yet it inherits all the features of its parent. No matter what instance/level of UIView you use, you can set/get any of their alpha (opacity), backgroundColor, or bounds properties.

The UISegmentedControl, UISlider, and UISwitch are all descendants of the UIControl, so you can set any of their isEnabled properties to true or false. A value of true allows the user to interact with the control. A value of false prevents the user from interacting with the control.

#ad

Let’s look at polymorphic methods. UITableViewController, UICollectionViewController, and UISplitViewController all inherit from UIViewController. Since UIViewController is the superclass of these other controller classes and it defines the present(_:animated:completion:), they all can display/present a view controller modally:

Now we have the background to define the term “polymorphism.” The origins (etymology) of the word tell us that the word’s parts come “From Ancient Greek poly (many) + morph (form).” In languages like Swift, classes that inherit from the same parent class can have methods with the exact same name, but each descendent’s method can behave differently. In what you’ll find to be a very compelling advantage of OOP, an instance (object) of the parent class can be assigned descendant objects, and yet the parent exhibits the behavior of the descendant.

Conversely, some descendents may not define a method defined by a parent class, so they automatically inherit that method and its corresponding functionality.

Check out my latest post on polymorphism.

OOP in-depth

Object-oriented programming evolved from the realization by some computer scientists that humans generally view the world in terms “objects” and/or “concepts.” Computing didn’t start this way despite the fact that philosophers/thinkers have known how we view the world for a very long time. We’ll simplify our discussion and refer to both terms as “entities” (note there is an ongoing debate regarding “the distinction between concept and object). Look at the definitions for the word “object:”

1 a : something material that may be perceived by the senses • I see an object in the distance.
b : something that when viewed stirs a particular emotion (as pity) …

2 a : something mental or physical toward which thought, feeling, or action is directed • an object for study • the object of my affection • delicately carved art objects …

If you visited the link for the definition of the word “object,” did you notice the last (6th) characterization of the term?

6 a : a data structure in object-oriented programming that can contain functions … as well as data, variables, and other data structures

OOP has become so ubiquitous that even general purpose dictionaries contain its definition.

Programmers first started solving problems using functional-, step-by-step-, linear-, and recipe-like methodologies. Some of these solution worked well but they were almost always too specific and intrinsically linked to the original problem that was to be solved. While it can be done, reusing functional code tends to be difficult. Procedural/recipe code is often a collection of single-purpose functions — and lots of them.

Functional programming depends too much on the how of the problem, and we know how easy it is to get lost in the details of how, and the many paths to how. OOP depends more on the what of the problem. If we understand and create a model of a problem that mirrors the tangible attributes and functions of that problem, the what, we’re more likely to understand and solve the problem.

When you think of an entity like a cat, what jumps into your mind? Do you think in terms of how the cat was constructed, its internal organs, the mechanics of how it walks or runs, how it digests its food, why it purs when it sits in your lap, etc? Or do you define a cat by its properties and its possible actions? I think of a cat as an independently-minded and friendly being who makes me happy.

My cats have properties like “has fur,” “is warm,” “has eyes,” “has ears,” “has four legs,” “is visible,” “occupies space,” etc. These cats can do things like “eat a lot,” “make me happy,” “run fast,” “jump amazing distances,” “play with my stuff,” “lay on top of my keyboard,” “type characters into my documents by walking on my keyboard…”

I see the world in terms of entities with properties and capabilities. If you’re looking to become one of the best of the best developers, then you should do the same. You should design and implement solutions to the products you’re building by thinking in terms of those products as entities with properties/data and capabilities/functions/methods.

Don’t think of a product as one huge entity. Divide and conquer and logically break the product into its constituent entities. For example, if you’re creating a new graphics design tool, after analysis, you’ll find that the end product will be made of entities like images, documents, colors, shapes, and drawing tools. OOP allows you to solve problems by thinking the way you normally think, modeling the world in terms of the entities/objects you perceive.

During design of your product, you must consider each of these entities as self-contained: The data and the methods should be encapsulated into one programming language construct, the class. A class is a blueprint containing and specifying the data and functions of the entity you are modeling. You must also consider how these classes will interact with each other. So you have to create an “interface” for each class that specifies which properties and methods are available (visible/accessible) to other classes in your product model. You make a class into a usable entity by declaring “object” or “instance” of that class.

How all that theory applies to my code

By building simple, well-defined classes, you create easily usable and comprehensible building blocks. When you want to hang a picture on the wall, you want to use a hammer and nails, not be pounding on toothpicks with the heels of a shoe. Despite its simplicity, a hammer has many uses.

Look at my IBMessageBoxDismiss base class. It contains everything needed to create, configure, and display an alert/message box. I’ve limited its functionality for a reason. It’s better to concentrate the core/powerful features of a class hierarchy towards the top of the tree. This keeps the base class(es) simple, general, and very reusable. You’ll find yourself looking at your base classes when it’s time to start a new project with writing a bunch of code. But you can design as many different and specialized classes as you want by using inheritance — creating descendants of those upper-level classes. In order for the specialized classes to tap into the power of their parents, they need only descend from them, just as I created the IBMessageBoxDismissAndOK and IBMessageBoxDismissAndOKWithTextField descendants of IBMessageBoxDismiss.

We just talked about keeping base classes general. Now we’ll talk about solving a variety of different problems by specializing those base classes through inheritance. Using inheritance, you create descendants of your base classes that solve problems that are more specific than the base classes were designed for. You also create discrete pieces of code with singular purposes, much more maintainable than huge blobs of spaghetti.

Take my message box hierarchy. The IBMessageBoxDismiss base class is great for general informational alerts. You use instances of that class to post simple messages to your user like “you entered the wrong password,” “please try again,” “you cannot delete that type of file,” or “this feature is not available.” Once a user sees the alert’s title and message, he/she has only one choice, that is to press the “Dismiss” button. For class IBMessageBoxDismiss, “Dismiss” just clears the alert from screen.

The first descendant of the base class, IBMessageBoxDismissAndOK, adds two powerful features to the base (on top of inheriting everything from the base). First, it adds the ability for the user to choose between two buttons, “Dismiss” for the default cancel function and “OK” for taking some action. Second, the “OK” button can execute a closure that you specify. Remember that closures allow you to pass about any kind of code almost anywhere.

The second descendent of the base class, IBMessageBoxDismissAndOKWithTextField, adds all the functionality of IBMessageBoxDismiss and IBMessageBoxDismissAndOK — and it adds a UITextField. The user can tap the “Dismiss” or “OK” buttons — and you can access any string typed into the UITextField. The best way to process a string captured by the UITextField is to associate a closure with the “OK” button.

Swift 3 class syntax

I’ve talked extensively about OOP, but it is a technology that, if it is to be used and used wisely, must be done properly. Let’s write some preliminary code that will segue into my alert/message box class hierarchy. Before we write each block of code, we’ll go over the Apple definition of the Swift construct we’re encoding.

Defining a class
Here is Apple’s definition of a Swift class:

Classes and structures are general-purpose, flexible constructs that become the building blocks of your program’s code. You define properties and methods to add functionality to your classes and structures by using exactly the same syntax as for constants, variables, and functions.

Unlike other programming languages, Swift does not require you to create separate interface and implementation files for custom classes and structures. In Swift, you define a class or a structure in a single file, and the external interface to that class or structure is automatically made available for other code to use.

Let’s declare a base class, one with no parents, either at the top of a taxonomy, or a standalone class:

Adding properties to a class
Remember that I emphasized that classes need properties or data. Here’s Apple’s definition of Swift class properties:


Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance…

I’m not going to get any deeper into OOP doo–doo (like computed properties). Let’s add a non-optional property to a class:

Class initialization
So where did that init method come from and why did I put it there? I could’ve avoided the init method by making the property “a” into an optional, but I didn’t. I believe a class should explicitly force the user to provide initial values for its properties when an instance (object) is created. This practice makes the class safer to use as it cannot be instantiated without some preparation. In other words, don’t give birth to a baby unless you’re willing to raise a child into adulthood.

See Apple’s discussion of initialization:

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

You implement this initialization process by defining initializers, which are like special methods that can be called to create a new instance of a particular type. Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.

Follow the link I just provided and find out about “designated initializers and convenience initializers.” I leave this to you as an assignment. Please read up on initializers before starting the section on “Classes and inheritance.”

Access control to class properties and methods
Why did I decorate my declaration of the “a” variable with the fileprivate keyword? Remember our discussion of “Hiding complexity (encapsulation)?” I don’t want outside entities knowing about or mucking with/corrupting the guts of my classes. I do want my classes to be understandable and easy to use. The fileprivate keyword keeps my properties (and possibly methods) from being accessible outside of the file in which they were declared. If there’s a need for access to say, a property, then that should be done through a getter (which should probably have a corresponding setter). Here’s Apple’s discussion of access control:

Access control restricts access to parts of your code from code in other source files and modules. This feature enables you to hide the implementation details of your code, and to specify a preferred interface through which that code can be accessed and used.

You can assign specific access levels to individual types (classes, structures, and enumerations), as well as to properties, methods, initializers, and subscripts belonging to those types. Protocols can be restricted to a certain context, as can global constants, variables, and functions. …

Swift’s access control model is based on the concept of modules and source files.

Please read the contents of the link.

Classes and inheritance
Apple defines inheritance as:

A class can inherit methods, properties, and other characteristics from another class. When one class inherits from another, the inheriting class is known as a subclass, and the class it inherits from is known as its superclass. Inheritance is a fundamental behavior that differentiates classes from other types in Swift.

Classes in Swift can call and access methods, properties, and subscripts belonging to their superclass and can provide their own overriding versions of those methods, properties, and subscripts to refine or modify their behavior. Swift helps to ensure your overrides are correct by checking that the override definition has a matching superclass definition.

Look at the top of the ViewController.swift file in my downloadable project (and in Code Sample 2 below). I didn’t even do anything except create a new project, and Xcode defined an inherited class, a subclass, for me automatically:

Let’s generalize class definitions. We’ll first declare a base class and then we’ll declare a subclass of the base class:

baseClassName is now the superclass of childClassName.

Classes and polymorphism
First, review my section above on “Abstraction and polymorphism.” Then, notice that my downloadable source code and Code Sample 1 below exhibit polymorphism through the “virtual” method “getTitle.” Notice that “getTitle” is defined as a general method in class IBMessageBoxDismiss. But it is redefined with (trivially) different behavior using the override keyword in class IBMessageBoxDismissAndOK, a subclass of IBMessageBoxDismiss, and is again redefined with (trivially) different behavior using the override keyword in class IBMessageBoxDismissAndOKWithTextField, a subclass of IBMessageBoxDismissAndOK.

Notice that we have three classes in the same inheritance tree (hierarchy) that all have the method “getTitle” but define it differently. This is an extremely powerful mechanism for extending classes through inheritance. Notice that a redefinition of “getTitle” can provide new functionality, but it can also make use of its superclass’s version of the same method. Each class in the inheritance tree has the option to inherit its parent/superclass’s definition of “getTitle” as-is, or it can completely redefine “getTitle,” or it can mix some superclass code with its own code, or it can even be prevented from overriding its superclass’s “getTitle” behavior (see the “Preventing Overrides” section at the bottom of this Apple link).

If you look at the code in the IBMessageBox.swift file in my downloadable project and/or in Code Sample 1 below, you’ll see that all my three IBMessageBox* classes’ initializers accept a UIViewController as a parameter. I leverage polymorphism here, allowing you to call either of my class hierarchy’s display() or display(_:message:) methods from any UIViewController or one of its descendants, UITableViewController, UICollectionViewController, UISplitViewController, etc. Since UIViewController and its children controller classes define the present(_:animated:completion:) virtual method, the display() or display(_:message:) methods all can display/present my classes’ UIAlertController (alert/message box) modally. Here are examples of my IBMessageBox* classes’ init and display method signatures (see highlighted lines, 7, 11, 29):

Check out my latest post on polymorphism.

Creating objects (instances of a class)
Remember that a class is a blueprint containing and specifying the data and functions of the entity you are modeling. Classes don’t spontaneously pop into existence for your convenience. You must declare an instance of a class (an object), instantiate it, and then call its methods from your code. See Code Sample 2 and/or ViewController.swift. Notice that in class ViewController’s viewDidLoad() method I create a class-level instances of IBMessageBoxDismiss and IBMessageBoxDismissAndOK. Because I created these objects at the class level, the message boxes/alerts they represent can be used throughout the life of the ViewController. There’s no need for constant configurations/initializations of UIAlertControllers (as is common but silly developer practice). I can pop up message boxes with different titles and messages at any time. But… I also have the option to create one-off alerts within individual methods. It all depends on the developer’s needs. Here’s a snippet showing class-level instantiation of two of my alert classes:

#ad

Sample Swift OOP code

Here’s the message box, technically UIAlertController, class hierarchy:

Code Sample 1. Message box class hierarchy.

Here’s the UIViewController subclass where I exercise the message box classes:

Code Sample 2. UIViewController subclass for testing message boxes.

OOP concepts can be difficult to grasp for neophytes, but if you create some practice class hierarchies and try designing new code/projects based around classes, not functions, you’ll get there.

[Download Xcode 8.2.1 project with full Swift 3 source from GitHub.]

#ad

Author: Andrew Jaffee

Avid and well-published author, software engineer, designer, and developer, now specializing in iOS mobile app development in Objective-C and Swift, but with a strong background in C#, C++, .NET, JavaScript, HTML, CSS, jQuery, SQL Server, MySQL, Oracle, Agile, Test Driven Development, Git, Continuous Integration, Responsive Web Design, blah, blah, blah ... Did I miss any fad-based catch phrases? My brain avatar was kindly provided by https://icons8.com under a Creative Commons Attribution-NoDerivs 3.0 Unported license.

Leave a Reply

Your email address will not be published. Required fields are marked *