Write a post

Enjoy this post? Give Cesar Tardaguila a like if it's helpful.

Enumerations and mappings to other enumerations

Published Mar 17, 2016Last updated Apr 12, 2017
Enumerations and mappings to other enumerations

Swift enumerations are cool, there is no doubt about it.

A lot has been written about enumerations, but the thing is, the more I use them, the more (neat) use cases I find for them.

What’s All the Fuss about Enumerations?

Enumerations can implement instant methods and static methods, conform to protocols, declare variables, initialisers, and support extensions.

All of that, by itself, might not seem like it is too big of a deal, but when we put it all together, we can use all these great powers to achieve our ultimate goal as engineers: cleaner code.

Show me an example

At the moment, I am writing a thin layer (in Swift) built on top of a third party library (written in Objective-C). But the fact that the third party library is written in Objective-C is not that relevant to this example.

Let’s assume the third party library, provides a method with this signature:

-(void) track: (ThirdParty) playbackEvent

Basically, what I need to do is track the progress of a video that is playing in a video player, according to something like this:

func trackProgress(progress: NSTimeInterval, duration: NSTimeInterval) {
    let quartile = progress / duration * 100
    
    if quartile > 0 && quartile < 25 {
        thirdparty.track(.ThirdPartyOne)
    }
    
    if quartile >= 25 && quartile < 50 {
        thirdparty.track(.ThirdPartyTwo)
    }
    
    if quartile >= 50 && quartile < 75 {
        thirdparty.track(.ThirdPartyThree)
    }
    
    if quartile >= 75 {
        thirdparty.track(.ThirdPartyFour)
    }
}

That, only because the developer that wrote the third party library was careful to declare his enumeration using NS_ENUM, so my Swift code can map to it without too much pain.

But, first of all, my code is ugly as a naked cat. And second of all, breaking down playback progress in quartiles is something that I will have to do in a few different places across my codebase, so it makes sense to encapsulate it. In particular, it makes sense to have an enumeration on my side of the line, that model quartiles properly. Something like this:

enum Quartile {
    case First, Second, Third, Fourth
}

Cool, but not cool enough. If every time I need to track progress, I need to go through a switch like the one in the trackProgress method defined previously, I would’ the much better off.

So, what would I want to do, ideally? Well, all I need is a factory method, something I pass the current progress and the total duration, and returns the quartile we are in. Like this:

enum Quartile {
    case First, Second, Third, Fourth
    
    static func fromProgress(progress: NSTimeInterval, duration: NSTimeInterval) -> Quartile {
        let quartile = progress / duration * 100
        switch quartile {
            case 0..<25: return .First
            case 25..<50: return .Second
            case 50..<75: return .Third
            case 75...100: return .Fourth
            default: return .Fourth
        }
    }
}

What’s cool about this? I wanted a factory, and since enumerations support static methods, I can have a factory. Neat!

Now, I want to map my enumeration (Quartile) to the enumeration that the third party library expects (TrackingEventType), which, if you have been following the post, you already know was written in Objective-C

But, Swift enumerations support extensions, so..

extension Quartile {
    var trackingEvent: TrackingEventType {
        switch self {
            case .First: return .ThirdPartyOne
            case .Second: return .ThirdPartyTwo
            case .Third: return .ThirdPartyThird
            case .Fourth: return .ThirdPartyFourth
        }
    }
}

Why an extension? Well, mapping Quartile to TrackingEventType does not look like it should belong to the Quartile Type. However, it makes more sense to me to separate it to an extension: if I need more mappings, I can add more extensions (Open Closed Principle FTW!)

The best part of it is how my trackProgressMethod looks now:

    func trackProgress(progress: NSTimeInterval, duration: NSTimeInterval) {
        let trackEvent = Quartile.fromProgress(progress, duration: duration).trackingEvent
        thirsparty.track(trackEvent)
    }

Which happens to be way cleaner.

Final words.

The usual: embracing Swift completely implies a slight shift in mindset. But that shift it is completely worth the effort.

Discover and read more posts from Cesar Tardaguila
get started
Enjoy this post?

Leave a like and comment for Cesar

Be the first to share your opinion

Subscribe to our weekly newsletter