Safely enumerate all enum cases in Swift
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Project
Sources
.gitignore
.travis.yml
EnumList.podspec
LICENSE
README.md

README.md

EnumList

Build Status Coverage Status CocoaPods CocoaPodsPlatform Fastlane Swift License

Library to enumerate all enum cases (for String or Int RawValue).

EnumList does not relay non memory introspection so it is safe by design and becomes stable in the future Swift releases.

Swift versions

This branch works officially with Swift 4.

If you want to use Swift 3 please check swift3 branch and cocoapods version v0.1.x.

Installation

CocoaPods

Use CocoaPods to install it with the following command:

$ gem install cocoapods

To integrate EnumList into your project, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
  platform :ios, '9.0'
  use_frameworks!

  target 'TARGET' do
    pod 'EnumList/Core'
  end

Then, run the following command:

$ pod install

Prerequisites

In order to make your enum (with String rawValue) compatible with EnumList, add conformance to protocols EnumListStringRaw<NAME_OF_YOUR_ENUM.Values>, RawRepresentable and define nested struct Values.

Althouh you have free choose the name and place to define struct that conformances to StringEnumValues or IntEnumValues (e.g. YourEnumName.Values), it is recommended to keep it as a nested type.

private enum YourEnumName: EnumListStringRaw<YourEnumName.Values>, RawRepresentable{
   struct Values:StringEnumValues {
      typealias Element = YourEnumName
      static var allRaws:Set<String> = []
   }
   case caseNo1 = "case1"
   case caseNo2 = "case2"
   //case caseNo3 = "case2" - compile error: RawValues have to be unique
}

YourEnumName.Values.all //  Set([.caseNo1, .caseNo2]) 
YourEnumName.Values.allRaws //  Set(["case1", "case2"])

All your cases exist in static Set<YourEnumName> variable `YourEnumName.Values.all.

Creating enum

Enum from a literal

You can still create your enum instance as previously, with literal intialization init?(rawValue:):

let myCase = YourEnumName(rawValue: "case1") // myCase = .caseNo1

Enum from a String variable

If you cannot use literals (e.g. when you have only String instance passed you from somewhere else), you can create it using init?(raw:) initializer:

let someString = "case1"
...
let myCase = YourEnumName(raw: someString) // myCase = .caseNo1
let myOtherCase = YourEnumName(raw: "case1") // myOtherCase = .caseNo1

Enums with Int RawValue

EnumList works the same when dealing with Int rawValues:

private enum YourIntEnum: EnumListIntRaw<YourIntEnum.Values>, RawRepresentable{
   struct Values:IntEnumValues {
      typealias Element = YourIntEnum
      static var allRaws:Set<Int> = []
   }
   case caseNo1 = 1
   case caseNo2 = 3
}

YourIntEnum.Values.all // Set([.caseNo1, .caseNo2])
YourIntEnum.Values.allRaws //  Set([1, 2])

let myCase = YourIntEnum(rawValue: 1) // .caseNo1

Enums with "Default" RawValue

Same as with standard enum, you don't need to specify all rawValues manually. Compiler will fill it for you, with the same String raw values, as a name of a case. You can mix cases with custom/automatic rawValue:

private enum YourEnumName: EnumListStringRaw<YourEnumName.Values>, RawRepresentable{
  struct Values:StringEnumValues {
    typealias Element = YourEnumName
    static var allRaws:Set<String> = []
  }
  case caseNo1 // is equivalent to case caseNo1 = "caseNo1"
  case caseNo2 = "case2"
}

YourEnumName.Values.all //  Set([.caseNo1, .caseNo2]) 
YourEnumName.Values.allRaws //  Set(["caseNo1", "case2"])

All Raw Values

YourEnumName.Values.allRawValues for grabing all raw values (which are a Set<String> or Set<Int>, depending of your enum deifinition). Note: YourEnumName.Values.initialize() or YourEnumName.Values.all are not required to call before anymore.

Safety

Swift does not allow to create several enum cases with same literal. It applies also for EnumList:

private enum YourEnumName: EnumListStringRaw<YourEnumName.Values>, RawRepresentable{
  struct Values:StringEnumValues {
    typealias Element = YourEnumName
    static var allRaws:Set<String> = []
  }
  case caseNo1 = "case1"
  //case caseNo2 = "case1" - compile error: RawValues have to be unique
}

Swift 4 compatibility

EnumList is compatible with Swift4 out-of-the box.

In addition, it supports Codable protocol as seamlessly as "normal" enums --- just add conformance to Codable (or separatelly Encodable and/or Decodable):

private enum YourEnumName: EnumListStringRaw<YourEnumName.Values>, RawRepresentable, Codable{
  struct Values:StringEnumValues {
    typealias Element = YourEnumName
    static var allRaws:Set<String> = []
  }
  case caseNo1 = "case1"
  case caseNo2 = "case2"
}

Integration with Unbox

If you want use your enum with Unbox framework, include additional subspec, EnumList/Unbox to your Podfile:

target 'TARGET' do
  pod 'EnumList/Core'
  pod 'EnumList/Unbox'
end

then by conforming your enum to UnboxableEnum, it works out of a box:

private enum EnumForUnbox: EnumListStringRaw<EnumForUnbox.Values>, RawRepresentable, UnboxableEnum{
  struct Values:StringEnumValues {
    typealias Element = EnumForUnbox
    static var allRaws:Set<String> = []
  }
  case caseNo1 = "case1"
  case caseNo2 = "case2"
}

let dictionary:[String:Any] = ["data":"case1"]
let a = Unboxer(dictionary:dictionary)
let unboxEnum:EnumForUnbox = try a.unbox(key: "data") // .caseNo1

Running the tests

Xcode

Library is covered with unit tests: to run tests integrated with EnumListPoroject.xcodeworkspace project: install all CocoaPods dependencies (pod install in your terminal) and run default unit tests (⌘+U).

Fastlane

To test all unit tests from a commandline, call 'fastlane test' in the Project directory.

Deployment

Use fastlane to build a project: fastlane Readme

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

How does it work?

EnumList relies on a fact that that if we try to initialise an enum using init(rawValue:), Swift takes all values of its RawRepresentable (that have corresponding case) one by one and compare it with your rawValue, until finds equal value. Therefore, if we try to initialise with some rawValue that does not match any case, EnumList will have a chance to gather all rawValues with corresponding enum inside == operator implementation. This library provides generic struct (String or Int representable) that you define as dedicated RawRepresentable type for your enum.

You can find details in the following document.

Authors

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE file for details