Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 37 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save andyyhope/2fc5b6bee8ee1346f688 to your computer and use it in GitHub Desktop.
Save andyyhope/2fc5b6bee8ee1346f688 to your computer and use it in GitHub Desktop.
Swift Evolution - Enum Case Count Functionality

Swift Evolution Proposal

Author: Andyy Hope

Enum Values() Functionality (Update)

It has been brought to my attention that there was more use for the unintended values() functionality that I had outline in my "Other Languages" Java example below.

On the Swift Evolution mailing list, one developer outlined their requirement to loop through an array of enum case values to add different states to objects.

Another example where a values array would be useful if the developer wants to do something different for each different case, such as setting an image on a UIButton subclass for each different UIControlState

Updates :
Naming

Some people have mentioned that perhaps "values" isnt a good term, and suggested that "cases" be a term more suitable to the collection. I would also like to mention that the term "cases" sounds a lot more appropriate and would suggest this over "values" myself. However, for the sake of simplicity, i will keep referring it to "values" for the rest of this proposal.

CollectionType

On the Mailing List, there was some discussion about whether it should be an Array or a Set, I think it could depend on the enum Type. Such as, if it was an Int it could be an ordered Array, whereas if it was of type String perhaps a Set would be more suitable.

Alternative workarounds

Manual values array

enum SettingsSection : Int {
    case Connectivity
    case Notifications
    case General
    
    static func values() -> [SettingsSection] {
        return [.Connectivity, Notifications, General]
    }
    // or
    static let values : [SettingsSection] = [.Connectivity, Notifications, General]
}

for value in SettingsSection.values() {
    // Do stuff
}

It's a func because if it was a var it wouldnt compile for the reason

Enums may not contain stored properties

But theres also a static let example in there for too.

Anyway, for the same reasons that we dont want to handle the count function manually, also having to update this array each time has potential to be out-of-order and potentially unsafe if the enum is being extended by another file.

Enum Count Functionality

Introduction (Original Proposal)

It’s quite often that I find myself needing to know the size of an enum. It’d be really effective in a lot of cases (pun intended) to have some variable/function that returns the total count of any given enum. This would reduce the burden of developers to individually manage tasks where the count is required.

At the time of writing this, one prolific example I can think of is the UITableView section example, whereas I define the UITableView sections inside of an enum declaration and for individual UITableViewDataSource / UITableViewDelegate.

Like thousands of other developers and myself do is declare an enum (of type Int), to define the amount types of sections I'd do within my UITableView such as the following

enum SettingsSection : Int {
     case Connectivity
     case Notifications
     case General
}

And from here on, most UITableViewDataSource / UITableViewDelegate protocol functions will require some type of action depending on which section the given section / indexPath.section. Most of us know this type of pattern when writing Swift code for UITableView

But one gripe we all deal with is the very first UITableViewDataSource protocol inside of Xcode’s UITableViewController boilerplate code is the function

func numberOfSectionsInTableView(_ tableView: UITableView) -> Int

This is the most ideal candidate for such a count/cases/length functionality of an enum. The developer would simply have to call the count var/function to return an Int to the value of enum's case count. For example...

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return SettingsSection.count
}

I'm sure there are other applications for including such functionality out-of-the-box for Swift developers, if anyone reading this has any, please let me know so i can append the proposal to include them.

Alternative Workarounds

'Count' case

enum SettingsSection : Int {
     case Connectivity
     case Notifications
     case General
     case Count
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return SettingsSection.Count.rawValue
}

A few of us with experience in C-style languages would have come past this little hacky solution. In C-style languages this would have worked fine too, but because Swift's switch statements require the developer to exhaust all statements (or use default) this would add unnecessary bloat to our code base.

Also, because Swift enums can be of types other than Int, this also makes it more redundant within the language.

static Int

The best solution for this so far would be to declare a static Int constant inside the Enum declaration, which would essentially create an elegant solution solve this problem.

enum SettingsSection : Int {
     case Connectivity
     case Notifications
     case General
     static let count = 3
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return SettingsSection.count
}

But keep in mind, this is still just a magic number at its core, developers can still unknowingly change this and cause some type of irrational or unexpected behaviour during compile/run time

Other languages

Java

According to the docs, Java implements a function called length for its values property.

SettingsSection.values().length

As you can see, this does require instantiation of the enum, which would also be an acceptable caveat for such implementation of the feature.

Java Documentation - The best documentation i could find on the net where Java uses its enums that also have a length function.

Admittedly, the developer has to instantiate an array containing all the cases of the enum to get the functionality of the length function. Even so, it would still be a tiny caveat to having such a great feature within the language.

Perhaps even having the values() function is what could be used most within Swift, and the count function could be an added bonus. But at the time of writing this, i have not needed in the past or cannot think of any scenarios where i would iterate through the cases of an enum.

If anyone reading this has some examples, please let me know so i can append this documenet.

Either way, I believe both scenarios would provide great solutions to significant problems

Summary

So by now i feel that i have made a strong enough case for the Swift Evolution team and community to consider making the addition of a count or even the values() function within Swift enum. I look forward to hearing feedback from both parties.

Thanks for your time.

Andyy Hope
@andyyhope
@nabby26
Copy link

nabby26 commented Mar 15, 2017

+1 👍

@designatednerd
Copy link

FWIW you can create a workaround for this using protocols in Swift 3 (huge props to @loganwright for coming up with the idea for the int one after I demoed doing it the old fashioned way): https://gist.github.com/designatednerd/390016f38c7f9559484c3bdcace066e1

But yes I would absolutely be in favor of dropping this workaround and using something in the standard lib for these.

@marekmako
Copy link

+1

@hsilived
Copy link

hsilived commented May 1, 2017

+1

@Holokai
Copy link

Holokai commented Jun 4, 2017

+1

@akerenyi
Copy link

akerenyi commented Jun 6, 2017

+1

@srea
Copy link

srea commented Jun 14, 2017

+1

@hyerra
Copy link

hyerra commented Jun 14, 2017

+1

@imairi
Copy link

imairi commented Jun 15, 2017

+1

@SuperWomble
Copy link

SuperWomble commented Jun 22, 2017

+1000 or whatever it takes to get this happening. It's a sore spot in Enum usage.

@christopherkriens
Copy link

+1

@samratshaw
Copy link

+1

@DavidLari
Copy link

+1

@anieduard
Copy link

+1

@superstever
Copy link

+1

@juanparati
Copy link

+1 👍

@jacobmbarnard
Copy link

+1

Copy link

ghost commented Sep 9, 2017

+1

@brookslyrette
Copy link

+1

@nunogoncalves
Copy link

+1
I've been thinking why this isn't in the language yet, but I saw a great point... associated values... that might be hard to deal with. :/

@crsantos
Copy link

👍

@wolffan
Copy link

wolffan commented Oct 2, 2017

👍

@tettoffensive
Copy link

👍

@blessingLopes
Copy link

👍

@andeeliao
Copy link

+1

@chungbkhn
Copy link

extension RawRepresentable where Self: Hashable {

// Returns the number of elements in a RawRepresentable data structure
static var elementsCount: Int {
    var i = 1
    while (withUnsafePointer(to: &i, {
        return $0.withMemoryRebound(to: self, capacity: 1, { return
            $0.pointee })
    }).hashValue != 0) {
        i += 1
    }
    return i
}

}

@DavidLari
Copy link

+1

@MartinP7r
Copy link

+1

@nazmulcuet11
Copy link

+1

@dinneo
Copy link

dinneo commented Mar 12, 2020

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment