Creating Traits or Mixins in Swift

This is an old post!

This post is over 2 years old. Solutions referenced in this article may no longer be valid. Please consider this when utilizing any information referenced here.

Object oriented programming is great, but sometimes things don’t fit neatly into a superclass/subclass hierarchy. You may have a piece of code that would be needed in several contexts, but for technical reasons beyond your control you cannot merge them into a single hierarchy.

Some languages have the concept of multiple inheritence, where a subclass can specifically inherit from several parents. But this has it’s own set of problems. Many other languages, however, solve this through the use of traits or mixins. These allow us to have a set of methods that are basically copied into the object at compile time. This way they can be used anywhere they are needed.

Swift doesn’t have the concept of mixins or traits per se. But, starting with Swift 3, you can get very equivalent functionality using protocol default implementations.

Protocol Default Implementations

Swift is very big on protocols. They are used everywhere, so by now you should be reasonably familiar with protocols. Swift is also big on extensions, which allow you to extend existing objects (even those whose source you do not have access to) with your own functionality.

All protocol default implementations are is a merging of these two things. So you create a protocol, then extend the protocol with your functionality.

protocol DoesSomething {
    public func doSomething()
}

extension DoesSomething {
    public func doSomething() {
        print("Hello world!")
    }
}

Pretty easy, huh? And using it is just as easy:

class Foo: DoesSomething {
    public func printSomething() {
        doSomething()
    }
}

let f = Foo()
f.printSomething()

Should print:

Hello world!

A Word of Warning

I sometimes think that traits/mixins/protocol default implementations may be an anti-pattern. My conclusion so far has been (like most things), “it depends on how they are used” if they are an anti-pattern or not.

My main problem with them is that they hide functionality in a way that might not be entirely clear to another developer (or even you, months later when you go to fix a bug). While the inheritence model of object-oriented programming is implicitly understood by most developers, traits throw a monkey wrench into that. They’re not in the hierarchy, but are brought in at compile time.

Especially if I’m in an object that descends from an object that pulls in a trait, at that point it is usually faster for me to just search the repository for the method implementation rather than try to find where it was done by tracing back through the parents. I have to check all the parents and any traits they pull in at any level. And changing the trait may have unintended effects that may be difficult find and fix because it is used in many contexts.

I guess what I’m saying is, it is very easy to create unmaintainable spaghetti code using this kind of functionality. It can be tempting to use this functionality as a code organization method. I disagree with that approach for the reasons listed above except in some very, very specific and limited circumstances. Tools like these are best used sparingly, when you literally have no other option because code is needed in multiple places that cannot otherwise descend from a common ancestor.

But that can happen, especially when using code or system APIs that you don’t control (such as Cocoa and CocoaTouch). Sometimes, you really do have no choice, so I’m glad this functionality is here.

Comments (0)

Interested in why you can't leave comments on my blog? Read the article about why comments are uniquely terrible and need to die. If you are still interested in commenting on this article, feel free to reach out to me directly and/or share it on social media.

Contact Me
Share It
Swift
It usually doesn’t take beginning macOS/iOS developers long to discover NotificationCenter and see it as the solution to every single problem of passing data around to different controllers. And NotificationCenter is great, but it has some downsides. Notably, it is very easy to introduce retain cycles (and memory leaks) unless you are very careful to track and free the listener when the object is released. This has bitten me on several occasions. In general, excessive use of NotificationCenter ends up creating a difficult to maintain app where it is not entirely clear what is responding to what and where.
Read More
Swift
Somewhat related to my previous post about responder chains, sometimes it is useful to be able to debug what all is in the responder chain at any given time. As a good rule of thumb, all ancestor views of a view are in that view’s responder chain, as well as (usually) the related controllers.
Read More
Swift
The responder chain is one of those parts of macOS and iOS development that may seem a little strange if you have not done any GUI programming before. Briefly, a responder chain is a hierarichy of objects that can respond to events. So, for example, a click or a tap might be passed up the responder chain until something responds to the action. But, the responder chain is more than just UI events. We can pass our own custom events up the responder chain as well!
Read More