Make the most of enumerations in Swift

Enumerations have changed a lot between Objective-C and Swift. We can easily forget how useful and powerful it can. I wanted to get back to it through simple examples to make the most of it.

Let’s assume we have an image service provider that can take width as parameter. The goal here is to standardise the image size we can use with enum.

// assuming we have a service returning a width 300px
class ImageProvider {
    static func fetch(name: String, width: CGFloat, completion: ((UIImage?, Error?) -> ())? = nil) {
        // request ...
    }
}

ImageProvider.fetch(name: "image.png", width: 300, completion: completionHandler)

It’s great to let some freedom to developers when requesting images. However, it doesn’t help me here to know which size is already used in my iOS app. That is where enum can help us.

Let’s start first with syntax

enum ImageSize {
    case small
    case medium
    case large
}

We can’t use it yet in our ImageProvider since it was based on CGFloat type. To make it available, I’m going to use raw values.

enum ImageSize : CGFloat {
    case small = 300
    case medium = 500
    case large = 900
}

ImageProvider.fetch(name: "image.size", width: ImageSize.small.rawValue, completion: completionHandler)

It’s a bit better, we can now understand we’re requesting a small size. However, the method is not restricted enough, we can still use any kind of value in it. To add this restriction, I’m using a value property to work as equivalent as a raw value.

enum ImageSize {
    case small
    case medium
    case large

    var value : CGFloat {
        switch self {
        case .small:  return 300
        case .medium: return 500
        case .large:  return 900
        }
    }
}

class ImageProvider {
    static func fetch(name: String, width: ImageSize, completion: ((UIImage?, Error?) -> ())? = nil) {
        let widthSize = width.value
        // prepare request ...
    }
}

ImageProvider.fetch(name: "image.png", width: .small, completion: completionHandler)

Our image sizes are finally standardised! We gave a bit of structure to our request. But now thinking about it, I don’t want to recreate a new size every time I need a format. Using associated type, I can give a bit more of freedom to the enum.

enum ImageSize {
    case small
    case medium
    case large
    case custom(_: CGFloat)

    var value : CGFloat {
        switch self {
        case .small:  return 300
        case .medium: return 500
        case .large:  return 900
        case .custom(let width): return width
        }
    }
}

ImageProvider.fetch(name: "image.png", width: .custom(1200), completion: completionHandler)

Wait, is it not with what we started from beginning? I was already able to customise the image size.

Fair point! My goal was to give an alternative approach using enumeration properties.

With my first version, it let any developers create a image request with a custom size. It potentially means that every view will have a specific size, and if you have a caching system based on the url generated, you’re going to cache multiple times the same image. Not so great.

With the last version of my code, I’m sure I will think twice before using a custom size for an image. My first thoughts would be to use one of the given standard. It’s easy to reuse, easy to replace and if none match to my purpose, I’ll still be able to create a custom one.

I hope those examples illustrated a bit more how useful and handy are enums in Swift.

© 2023 Benoit Pasquier. All Rights Reserved
Author's picture

Benoit Pasquier

Software Engineer 🇫🇷, writing about career development, mobile engineering and self-improvement

ShopBack 💰

Singapore 🇸🇬