Skip to content

Instantly share code, notes, and snippets.

@designatednerd
Last active August 3, 2018 06:14
Show Gist options
  • Save designatednerd/390016f38c7f9559484c3bdcace066e1 to your computer and use it in GitHub Desktop.
Save designatednerd/390016f38c7f9559484c3bdcace066e1 to your computer and use it in GitHub Desktop.
Swift 3 Helpers for getting all the cases of a given enum [Swift 3]
import Foundation
/**
A swift protocol to make any Int enum able to count its cases.
Super-useful for making enums to help you deal with sections in tableviews without having to maintain a case for the count of the enum.
Originally developed by Logan Wright for Swift 2.0 here:
https://gist.github.com/LoganWright/c8a26b1faf538e40f747
Based on this and the subsequent 3 slides from my talk about
Laziness-Driven Development at CocoaConf Boston 2015:
https://speakerdeck.com/designatednerd/laziness-driven-development-in-ios-cocoaconf-boston-september-2015?slide=28
*/
public protocol CountableIntEnum {
/// - parameter rawValue: The raw Int value. Matches the initializer of RawRepresentable without the Self restrictions.
init?(rawValue: Int)
}
//MARK: Default Implementation
public extension CountableIntEnum {
/**
- returns: A generated array of all the cases in this enum. Mostly useful for calling with
.count so you can get the number of items in this enum.
*/
public static var AllCases: [Self] {
var caseIndex: Int = 0
let generator: AnyIterator<Self> = AnyIterator {
let _generator = Self(rawValue: caseIndex)
caseIndex += 1
return _generator
}
return Array(generator)
}
/**
Method to consolidate fatal erroring out if something ain't there.
- parameter index: The index (indexPath.row or indexPath.section, usually) of the thing you want to grab.
- returns: The retrieved enum case. Fatal errors if it can't find it.
*/
public static func forIndex(_ index: Int) -> Self {
guard let rowOrSection = Self(rawValue: index) else {
fatalError("Issue unwrapping row.")
}
return rowOrSection
}
}
import Foundation
public extension RawRepresentable {
/**
Method to consolidate fatal erroring if something that should definitely be in an enum which conforms
to `RawRepresentable` isn't there.
This means at the call site where you're trying to instantiate from a raw value instead of
```
guard let section = Sections(rawValue: sectionIndex) else { fatalError() }
switch section {...
```
You can just do
```
switch Sections.forValue(sectionIndex) {...
```
- parameter value: The raw value (indexPath.row or indexPath.section, usually) of the thing you want to grab.
- returns: The retrieved enum case. Fatal errors if it can't find it.
*/
static func forValue(_ value: RawValue) -> Self {
guard let type = Self.init(rawValue: value) else {
fatalError("Could not instantiate a \(Self.self) with value \(value)")
}
return type
}
}
import Foundation
/// A protocol to allow grabbing an array of all the cases of a string enum.
/// Inspired by: http://stackoverflow.com/a/32429125/681493
public protocol StringCaseListable {
/// - parameter rawValue: The raw string value. Matches the initializer of RawRepresentable without the Self restrictions.
init?(rawValue: String)
}
//MARK: - Default Implementation
public extension StringCaseListable {
/// - returns: A generated array of all the cases in this enum.
static var AllCases: [Self] {
var caseIndex: Int = 0
// Create an iterator
let generator: AnyIterator<Self> = AnyIterator {
// In which each time it goes around, the case index is used
let current: Self = withUnsafePointer(to: &caseIndex) {
// To grab the appropriate memory for one of this type, and grab it's pointee
$0.withMemoryRebound(to: Self.self, capacity: 1) { $0.pointee }
}
caseIndex += 1
return current
}
return Array(generator)
}
}
@designatednerd
Copy link
Author

Note that in Swift 4.2, the CaseIterable protocol will take care of generating your list of items in the enum. Once that lands, you should definitely use that instead of either StringCaseListable or CountableIntEnum.

The RawRepresentable extension just added will give you the ability to centralize fatal errors on any RawRepresentable type. If you want to constrain it to just CaseIterable types, you can do that by updating the declaration to:

public extension RawRepresentable where Self: CaseIterable {

@alexiscn
Copy link

alexiscn commented Aug 3, 2018

when enum implement CodingKey

enum Food: String, CodingKey, StringCaseListable {
    case apple = "Apple"
    case banana = "b"
}

for key in Food.AllCases {
    print(key)
}
 // prints Food(stringValue: "Apple", intValue: nil)
 // prints Food(stringValue: "b", intValue: nil)

But I excepted prints apple and banana.

It seems CodingKey breaks down the description of Food and can not get original key anymore. Any suggestion ?

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