Creating Traits or Mixins in Swift

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.

Did something I wrote help you out?

That's great! I don't earn any money from this site - I run no ads, sell no products and participate in no affiliate programs. I do this solely because it's fun; I enjoy writing and sharing what I learn.

All the same, if you found this article helpful and want to show your appreciation, here's my Amazon.com wishlist.

Read More

Hierarchies: Finding Parents, Children and Descendents using 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.

Debugging the Responder Chain in 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.