iOS

Protocol Oriented Programming in Swift: Is it better than Object Oriented Programming?


We’re going to talk in-depth about protocol-oriented programming (POP) using Swift 4 in this article. This post is the second and final article in a two part series. If you haven’t read the introductory article, please do so before continuing onwards.

Today, we’ll discuss why Swift is considered a “protocol-oriented” language, compare POP and object-oriented programming (OOP), compare value semantics and reference semantics, consider local reasoning, implement delegation with protocols, use protocols as types, use protocol polymorphism, review my real-world POP Swift code, and finally, discuss why I’ve not bought 100% into POP.

A note on WWDC links

I’ve referenced at least three links into Apple Worldwide Developers Conference (WWDC) videos in this two-part series on POP. Clicking on these links takes you directly into a specific section of a video (and oftentimes starts playing that video at that time index), if you’re using Safari. If you’re not using Safari, you’ll have to glean the time index from the link itself, scan through the video, and/or look at the transcript for each video.

Why is Swift “protocol-oriented?”

Remember that in my introductory article on POP that I mentioned Apple’s assertion that “at its heart, Swift is protocol-oriented?” Well, trust me, it is. Why? Let’s talk a little comparative languages before I answer my own question.

It’s good for you to know a bit about other languages as you may find yourself at some point, for example, linking C++ libraries into your iOS apps. A number of my iOS and OS X apps link with C++ libraries as I have Windows versions of those apps. I’ve supported some seriously “platform-independent” apps over the years.

OOP languages have supported interfaces for years, and interfaces are similar to Swift protocols, but not equivalent.

Interfaces in these languages specify what methods and/or properties must be implemented by the class and/or struct that adopts (conforms to) a specific interface. I used “and/or” because, for example, C++ has no concept of an interface; rather one would use an abstract class; and, FYI, a C++ struct can inherit from a class. C# interfaces allow the specification of properties and methods and a struct can adopt an interface. Objective-C uses the term “protocol” instead of “interface,” and protocols can require methods and properties, but only a class can adopt a protocol, not a struct.

These interfaces and the Objective-C protocol don’t implement anything, they just specify requirements, serving as “blueprints” for the adopting classes and/or structs.

Protocols form the foundation of the Swift standard library. As I showed you in my first article on POP, protocols are the key ingredient in the POP recipe (methodology or paradigm).

Swift protocols have something that none of these other languages support: protocol extensions. From Apple:

Protocols can be extended to provide method, initializer, subscript, and computed property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function. …

By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification. …

You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension. …

You saw me use protocol extensions in my last article and you will again today. They are the secret sauce that makes Swift POP so powerful.

Protocols were important in iOS even before Swift. Remember my discussion of protocols like UITableViewDataSource and UITableViewDelegate that long-time iOS developers have adopted for years? Then also remember your everyday Swift programming language code.

It would be impossible to write Swift code without making use of protocols in the standard library. Take Array (a struct inheriting from 10 protocols), Bool (a struct inheriting from 7 protocols), Comparable (a protocol that inherits from another protocol and is the ancestor of many other Swift types), and Equatable (a protocol from which many Swift protocols and types inherit).

Spend some time reviewing the Swift standard library, following links to all the types, protocols, operators, globals, and functions. Make sure you look for the “Inheritance” sections on most all of the pages, and be sure to click on the “VIEW PROTOCOL HIERARCHY ->” links. You’ll see lots of protocols, links to protocol definitions, and diagrams of protocol inheritance.

Remember one very important point: Most code in the iOS (and OS X) SDKs comes in the form of class hierarchies. I believe many of the core frameworks we use are still written in Objective-C (and some C++ and C), like Foundation and UIKit. Take the UIButton class found in UIKit. Follow the inheritance tree using the “Inherits From” links found in Apple’s documentation pages from the leaf UIButton up to the root NSObject here: UIButton to UIControl to UIView to UIResponder to NSObject. Visually, it looks like this:

UIButton Inheritance

POP and OOP

The merits of OOP have been long extolled. Instead of regurgitating and defining them, I’ll list them here and provide a link to the detailed explanation I wrote about OOP as materialized in Swift here.

Note: If you’re reading this article and don’t understand OOP, I suggest you get up to speed before even considering POP.

OOP benefits include reusability, inheritance, maintainability, hiding complexity (encapsulation), abstraction, polymorphism, access control to a class, and access control to a class’s properties and methods. There are probably a few catch-phrases I’ve left out as so much, probably too much, has been said about OOP.

Simply put and without quibbling over inanities, OOP and POP share most of these attributes, with one major exception: Classes can only inherit from one other class while protocols can inherit from multiple protocols.

Because of the way OOP inheritance works, it’s probably a best practice to be limited to single inheritance. Multiple inheritance can get very messy very quickly.

On the other hand, protocols can adopt from one to many other protocols.

Why the push towards protocols? When large class hierarchies are built, a lot of properties and functionality can get inherited. Developers tend to add (and keep adding) general features to the top of the hierarchy — mainly to high-level ancestor classes. Mid-level and leaf-level classes tend to be more specific and not be functionality receptacles. Ancestor classes tend to be buckets for new functionality; oftentimes they become “polluted” or “bloated” with too many, extra, extraneous, and/or unrelated features. Mid-level and leaf-level classes end up inheriting a lot more features than they need.

These concerns about OOP aren’t written in stone. A good developer can avoid many of the OOP pitfalls I just enumerated. It takes time, practice and experience. For example, developers can overcome the functional bloat problem by adding instances of other classes as members to the classes they’re building rather than inheriting from those other classes (i.e., composition over inheritance).

A bonus from Swift POP: protocols can be adopted by value types like struct and enum, not just classes. We’ll discuss some advantages to using value types soon.

I do have some concerns about multiple protocol conformance. Are we trading one type of complexity and difficulty in understanding our code for another, i.e., trading the “vertical” complexity of OOP’s inheritance for the “horizontal” complexity of POP inheritance?

Compare the class inheritance hierarchy for UIButton, that I showed you earlier, to the protocol inheritance bloat of the Swift Array:

Local reasoning can’t apply to either of these complex entities. There are just too many pieces and relationships.

Value semantics versus reference semantics

As I mentioned in the last article, Apple is evangelizing for the related concepts of POP and value semantics. (They’re pushing one more ideal related to POP, as we’ll see below.) I showed you code last time, and will again today, that should make the meaning of “reference semantics” and “value semantics” obvious. Look at the code for my ObjectThatFlies protocol in last week’s post and today at my List, FIFO, LIFO, and related protocols.

That Apple engineer, Alex, says that we should use “value types and protocols to make your app better.” According to a section of code documentation entitled “Understanding Value Semantics” accompanying an Apple sample playground:

Sequences and collections in the standard library use value semantics, which makes it easy to reason about your code. Every variable has an independent value, and references to that value aren’t shared. For example, when you pass an array to a function, that function can’t accidentally modify the caller’s copy of the array.

Of course this applies to all types that use value semantics. I urge you to download and walk through this whole playground.

I’m not dropping classes, which exhibit reference semantics. I can’t. I have too much of my own class-based code. I help my customers with their millions of lines of class-based code. I do agree that value semantics are generally safer than reference semantics. As I develop new code, or refactor existing code, I’ll consider making the leap on a case-by-case basis.

Reference semantics can lead to “unintended sharing” of data by class instances (references). Some call this “unintended mutation.” There are techniques we can use to minimize the side effects of reference semantics, but I will grant that I’m looking more and more to value semantics.

Value semantics allow us to avoid unintended changes to variables, which is a really good thing. We’re avoiding side effects due to unintended mutation because “Every variable has an independent value, and references to that value aren’t shared.”

Since the Swift struct is a value type and can adopt protocols, and Apple’s pushing POP over OOP, there you have the reasoning behind the company’s evangelization for “protocol(s) and value oriented programming.”

Local reasoning

Let’s talk about a laudable goal, something Apple calls “local reasoning.” The topic was introduced by an Apple engineer named Alex at WWDC 2016 – Session 419, “Protocol and Value Oriented Programming in UIKit Apps.” Make this a third concept that Apple is promoting in conjunction with its push for POP.

I thought this was old news. Years ago, professors, coworkers, mentors, fellow developers, etc., had been talking about techniques like: never writing a function that doesn’t fit on one screen without scrolling (i.e., no longer than one printed page, and preferably shorter); breaking large functions into smaller ones; breaking large code files into smaller ones; using meaningful variable names; taking time for design before banging on a keyboard; making judicious and consistent use of spacing and indentation; the grouping of related properties and behaviors into classes and/or structs; and, grouping related classes and/or structs into organizational units like frameworks or libraries. But it was brought it up while Apple was explaining POP.

Alex told us:

Local reasoning means that when you look at the code, right in front of you, you don’t have to think about how the rest of your code interacts with that one function. You may have had this feeling before and that’s just a name for that feeling. For example, maybe when you just joined a new team and you have tons of code to look at but very little context, can you understand what’s going on in that single function? And so the ability to do that is really important because it makes it easier to maintain, easier to write, and easier to test, and easier to write the code the first time.

Ah, er, ah… Has that ever happened to you? I’m being facetious, because I have read some really well-written code developed by other people. I’ve written some very readable code. To be honest, after 30 years in the industry, most of the existing code I’ve had to support and/or enhance has not given me that warm and fuzzy feeling that Alex is describing. Oftentimes, I’ve become quite frustrated because I’ll look at a piece of code and have no idea what’s going on.

The Swift language source code is open/public. Please just give a quick glance at the following function, and please don’t spend three hours trying to understand it.

Can you honestly say you understood this code after taking a glance at it? I didn’t. I had to spend time reading through it several times and looking up things like function definitions. In my experience, code like this is ubiquitous and often just darn unavoidable.

Now let’s consider understanding a Swift type (granted, it’s not a function). Look at Swift’s Array definition. My goodness. It inherits from eleven protocols, BidirectionalCollection, Collection, CustomDebugStringConvertible, CustomReflectable, CustomStringConvertible, ExpressibleByArrayLiteral, MutableCollection, RandomAccessCollection, RangeReplaceableCollection, and Sequence. Click the “VIEW PROTOCOL HIERARCHY ->” link button and — holy cow, Batman! — look at that spaghetti.

The bottom line is that it’s a lot easier to achieve local reasoning if you’re able to start a new project and have your entire team buy in voluntarily to a consistent set of guidelines for best practices during software development. Refactoring, if done to small amounts of code at a time, presents another opportunity for achieving local reasoning. For me, like most everything else, refactoring is to be done judiciously and carefully, with Goldilocks motivation: not too hot and not too cold.

Keep in mind that’s you’re almost always going to be faced with some very complex business logic that, when translated into code, is going to require that a new team member receive some form of domain knowledge training and/or indoctrination before she/he can read your code fluently. She/he is most likely going to have to look up the definitions of some functions, classes, structs, enums, variables, etc.

Delegation and protocols

Protocols play and integral part in the delegation design pattern, ubiquitous throughout iOS. We don’t need to rehash it here. You can read my AppCoda post on the subject here.

Protocols as types and protocol polymorphism

I’m not going to spend a whole lot of time on these topics. I’ve told you a whole lot about protocols and showed you a lot of code. As an assignment, I want you to research how important it is, and how much flexibility it affords us, to be able to use Swift protocols as types (as in delegation), and because they exhibit polymorphism (i.e., if you had a factory model with many structs all conforming to protocols in the same family of protocols).

Protocols as types
Remember that in my article on delegates, I defined this property:

where LogoDownloaderDelegate is a protocol. I then called one of the protocol’s methods.

Protocol polymorphism
Just as in OOP, we can interact with multiple types conforming to the same family of protocols through a type conforming to the families’ parent protocol. This is best demonstrated through example code:

If you run the code in Playground, here is the console output:

Real-world UIKit applications of protocols

Let’s get down to brass tacks and write some Swift 4 code that’s being used in production iOS apps — my own apps. This code should get you started down the path of thinking in terms of building and/or extending code using protocols. What do we call that? “Protocol-oriented programming” or POP as I’ve been harping on continuously throughout two articles.

I’ve chosen to show you how to extend or enhance — whichever term you prefer — UIKit classes because 1) you’re probably so used to dealing with them and 2) it’s a bit more tricky to extend iOS SDK classes like UIView than it is if you were starting from your own custom classes.

I wrote all this UIView enhancement code in an Xcode 9 project based on the Single View App template.

Note I’m enhancing UIView with default protocol extensions — and the key to doing this safely is with what Apple calls “conditional conformance” (see also here). Since I’m only interested in extending the UIView class, let’s make the compiler enforce my interest and make it a requirement.

I often use the UIView class as a container to organize other UI elements in my app screens. Sometimes I use those container views ornamentally (visibly) to improve to look, feel, and layout of my UI.

Here’s an ani-GIF showing the result of applying three protocols I’ll create below to customize the appearance of the UIView class:

Notice that I’m following the “local reasoning” principle here. Each one of my protocol-based solutions can fit on one screen/page without scrolling. I hope you can read each as they don’t contain much code, but are powerful nonetheless.

Adding a default border to UIView

Suppose I’d like a bunch of instances of UIView to all have the same border — like in an app with a color theme. An example of this class is shown as the green colored top view in the image I just presented above.

To create, configure, and display an instance of SimpleUIViewWithBorder, I put the following code in an IBAction in my ViewController subclass:

I didn’t have to create a special initializer for this subclass of UIView.

Adding a default background color to UIView

Say I want several instances of UIView to all have the same background color. An example of this class is shown as the blue colored middle view in the image I just presented above. Notice I’m moving one step closer to a configurable UIView. Do you see where?

To create, configure, and display an instance of UIViewWithBackground, I put the following code in an IBAction in my ViewController subclass:

I didn’t have to create a special initializer for this subclass of UIView.

Adding a configurable border color to UIView

Now I want to be able to configure the border, both its color and thickness, for multiple instances of UIView. With the following implementation I can create as many groups of views, with differing border colors and thicknesses, as I want. An example of this class is shown as the red colored bottom view in the image I just presented above. There’s a cost to adding configurable properties to my protocol. I need to be able to initialize those properties, so I’ve added an init to my protocol. That means I’ve got to be able to call the UIView initializer, too. You’ll see when you read the code:

To create, configure, and display an instance of UIViewWithBorder, I put the following code in an IBAction in my ViewController subclass:

What I didn’t want to do

I didn’t create a generic piece of code like this:

This might be a valid in some situations, but I feel that it’s painting with with a very broad stoke. I would have lost a lot of granular control. There’s also a tendency for this type of construct to become a dumping ground for every enhancement related to UIView, in other words, a big bloated piece of code. The bigger it gets, the less readable and supportable it becomes.

I used classes — reference types — to subclass UIView in all the above UIKit-based helper protocols. Subclassing gave me direct access to everything in the parent UIView class, making my code clear, short, simple, and readable. If I would’ve used struct, my code would’ve been much more verbose. I leave it to you as an exercise to determine why.

What I can do

Keep in mind that all these default protocol extensions can be overridden in the extended class. This is best explained with an example and picture:

Notice my comment in the SimpleUIViewWithBorder immediately above in the class definition. Look at the top view in the ani-GIF shown here:

Real-world, generic data structures based on protocols

I’m very proud of the minimal amount of POP code I had to write to create fully-functional and generic stack and queue data structures that I’m now using in my own apps. For background on using Swift generics, please read my AppCoda article here.

Notice that I used protocol inheritance to help me build the more specialized FIFO and LIFO protocols from the more abstract List protocol. I then took advantage of protocol extensions to materialize the Queue and Stack value types. You can see instances of these struct types at work in the Xcode 9 playground shown below.

I wanted to show you how Apple advises that customization of protocols should be achieved mainly through adoption of other protocols, so I created the ListSubscript, ListPrintForwards, ListPrintBackwards, and ListCount protocols. They are currently simplistic, but would show promise in an actual app.

This multiple adoption of other protocols is meant to allow developers to add features to a codebase without “polluting” or “bloating” it with too many, extra, extraneous, and/or unrelated features. Each of these helper protocols is standalone. If added as classes above leaf level in an inheritance hierarchy, these features would automatically be inherited by at least some other classes in the family tree depending on their positions in the tree.

I’ve told you enough about POP for you to read and understand this code. I’ll grant you one hint about how I made my data structure types generic: the definition of “associated types:”

When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that is used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

Here’s the code:

Console output from previous code snippet

I haven’t bought 100% into POP

In one of the WWDC videos on POP, an engineer/presenter says “we have a saying in Swift. Don’t start with a class. Start with a protocol.” Yeah, well, maybe. This guy gets into some long-winded discussion about getting a binary search to work using protocols. I somehow doubt this is many of my readers’ most pressing concern right now. Are you losing sleep over it?

This smells like a contrived problem cooked up to be supposedly looking for a POP solution. Maybe it is valid; maybe the solution has merit; I don’t know. My time is precious and I don’t have a lot to spare for ivory tower theorizing. If looking at some code and understanding it takes more than 5 minutes, then I know it doesn’t fall under the category of Apple’s “local reasoning” principle.

It’s always a good idea to keep an open mind to new methodologies when you’re a software developer like me, and managing complexity is a mainstay of your livelihood. I am by no means opposed to making money, but it’s good to put things in perspective. Always remember that Apple is a business, a big business, on a mission to make lots of money, with last Friday’s total market cap value at closing of about $837 BILLION, with several hundred billion in cash and cash equivalents. It’s in their interest to get everyone roped into Swift, and one way companies try to rope people into their ecosystems is to offer products and services that no one else supposedly offers. Yeah, yeah, yeah, Swift is open source, but Apple makes big money from the App Store and apps are what makes all of Apple’s devices useful, and many, many app developers are moving to Swift.

I don’t see any justification for becoming a POP-only programmer. I see some problems with POP just as I do with many other technologies, even OOP. We’re modeling reality. At best we’re approximating reality. There are no perfect solutions. Throw POP into your developer’s toolbox along with the rest of the few good ideas people have come up with over the years.

Conclusion

Now that I’ve got some of that 30-years-of-software-experience cynism off my chest, I can speak more calmly. You should look into protocols and POP. Above all, design and write your own POP code.

I’ve already spent quite a bit of time experimenting with POP and am already using the protocols I presented in this article, like SimpleViewWithBorder, ViewWithBackground, ViewWithBorder, List, FIFO, and LIFO, in my own production apps. POP is powerful.

As I mentioned in the introductory post to this article, learning and adopting a new technology like POP is not an all or nothing proposition. POP and OOP can not only co-exist, they can compliment each other.

So get out there, experiment, practice, study… Above all, make the best of life and work and enjoy yourselves!

iOS
Developing iBeacons Apps with Swift
iOS
Understanding Key-Value Observing and Coding
Swift
What’s New in Swift 4 by Example
Shares