enum RandomTips { case EnumFuncs

Table of Contents

case EnumFuncs

I looove enums. They allow you to explicly define enumerations without resorting to stringly typing or falling back to simple Ints. However, they don’t have inherent compatibility with some subsystems with Apple’s dev system, which includes things like UISegmentedControl.

Since all you get out of a UISegmentedControl is an Int representing the current index selected, there are two potential ways built into the current system to account for that. One could be

enum SelectedSegmentOptions {
    case A
    case B
    case C
}

@IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {

    let myValue: SelectedSegmentOptions

    switch sender.selectedSegmentIndex {
    case 0:
        myValue = .A
    case 1:
        myValue = .B
    case 2:
        myValue = .C
    default:
        myValue = .A
    }
}

This has one upside and various fallbacks. The upside is that it’s easy. However, the downsides outweigh that, in my opinion.

  • If you need to associate indexed values with various enumerated types in several places in your project, you have to remember which index goes to with every enum and keep them consistent.
    • Additionally, If, at some point, you decide to reorder the segmented index in the UI where index 0 is now associated with .B, you have to remember everywhere in your app that you associated the two and modify them. The same applies if you append or remove an option from the segmented index.
  • We still have to deal with the default case since Ints aren’t exhaustable (and again could snowball modifications in your app if you decide to change the default value).

A better way is to do this:

enum SelectedSegmentOptions: Int {
    case A //now has a value of 0
    case B //now has a value of 1
    case C //now has a value of 2
}

@IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
    guard let myValue = SelectedSegmentOptions(rawValue: sender.selectedSegmentIndex) else { return }
}

The upside here is that you’re not manually associating the raw index value with the enums, but you DO have to be mindful that the order of your enum matches the order of the UISegmentedControl options. The other downside is that you’re resultig in an optional, which may or may not matter for your purposes, but can often lead to irritations with needing to unwrap.

Finally, I propose this as an alternative good approach:

enum SelectedSegmentOptions {
    case A
    case B
    case C

    static func getOption(for value: Int) -> SelectedSegmentOptions {
        switch value {
        case 0:
            return .A
        case 1:
            return .B
        case 2:
            return .C
        default:
            return .A
        }
    }
}

@IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
    let myValue = SelectedSegmentOptions.getOption(for: sender.selectedSegmentIndex)
}

It’s definitely not without its downsides:

  • It always gives you an enum back
    • This means that if you add an option to your segmented control and forget to update both the enum and the getOption function, your app (likely) won’t crash and will proceed forward using the default value from the switch statement.
  • You’re still manually associating the values

But there are pros too!

  • No dealing with optionals
    • If something unexpected happens, you can still proceed with a reasonable default enum value
  • While you’re still manually associating, it’s in a centralized area
    • You won’t need to fish around your project for every place this concept is used since you’ll manage it all in one area
  • Now we can use an exhaustive switch almost directly from a segmented control!
@IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
    let myValue = SelectedSegmentOptions.getOption(for: sender.selectedSegmentIndex)

    switch myValue {
    case .A:
        //Do .A things
    case .B:
        //Do .B things
    case .C:
        //Do .C things
    }
}

I want to clarify that I’m not suggesting that you only ever use this pattern, but that you keep it in mind and use it when appropriate. Sometimes this will be the right way to handle your index associations, and sometimes it’ll be better to use the rawValue option. Use your Swifty powers wisely!

Leave a Reply