Swiftpack.co -  Package - brightdigit/Options
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
brightdigit/Options
Sometimes there are situations where you want to use an Enum in an OptionSet or you want Enum backed by a RawType of Int but also have String labels as well
.package(url: "https://github.com/brightdigit/Options.git", from: "0.2.0")

Options

Options

Swift Package for more powerful Enum types.

SwiftPM Twitter GitHub GitHub issues

macOS ubuntu Travis (.com) Bitrise CircleCI

Codecov CodeFactor Grade codebeat badge Code Climate maintainability Code Climate technical debt Code Climate issues Reviewed by Hound

Table of Contents

Introduction

Options provides a features to Enum and OptionSet types such as:

  • Providing additional value types besides the RawType rawValue
  • Being able to interchange between Enum and OptionSet types
  • Using an additional value type for a Codable OptionSet

Example

Let's say you have Enum type:

enum ContinuousIntegrationSystem {
  case github
  case travisci
  case circleci
  case bitrise
}

We want two things:

  • Use it as an OptionSet so we can store multiple CI sytems
  • Store and parse it via a String

If OptionSet requires an Int RawType, how can we parse and store as String?

With Options we can enable ContinuousIntegrationSystem to do both:

enum ContinuousIntegrationSystem: Int, MappedValueCollectionRepresented {
  case github
  case travisci
  case circleci
  case bitrise
  
  typealias MappedType = String
  
  static let mappedValues = [
    "github",
    "travisci",
    "circleci",
    "bitrise"
  ]
}

typealias ContinuousIntegrationSystemSet = EnumSet<ContinuousIntegrationSystem>

let systems = ContinuousIntegrationSystemSet([.travisci, .github])

Installation

Swift Package Manager is Apple's decentralized dependency manager to integrate libraries to your Swift projects. It is now fully integrated with Xcode 11.

To integrate Options into your project using SPM, specify it in your Package.swift file:

let package = Package(
  ...
  dependencies: [
    .package(url: "https://github.com/brightdigit/Options", from: "0.1.0")
  ],
  targets: [
      .target(
          name: "YourTarget",
          dependencies: ["Options", ...]),
      ...
  ]
)

Usage

Setting up a MappedValueRepresentable Enum

So let's say we our enum:

enum ContinuousIntegrationSystem: Int {
  case github
  case travisci
  case circleci
  case bitrise
}

We want to be able to make it available as an OptionSet so it needs an RawType of Int. However we want to decode and encode it via Codable as a String.

Options has a protocol MappedValueRepresentable which allows to do that by implementing it.

enum ContinuousIntegrationSystem: Int, MappedValueRepresentable {
  case github
  case travisci
  case circleci
  case bitrise
  
  static func rawValue(basedOn string: String) throws -> Int {
    if (string == "github") {
      return 0
    } else {
      ...
    } else {
      throw ...
    }
  }
  
  static func mappedValue(basedOn rawValue: Int) throws -> String {
    if (rawValue == 0) {
      return "github"
    } else {
      ...
    } else {
      throw ...
    }
  }
}

This can be simplified further by using MappedValueCollectionRepresented.

Using MappedValueCollectionRepresented

By using MappedValueCollectionRepresented, you can simplify implementing MappedValueRepresentable:

enum ContinuousIntegrationSystem: Int, MappedValueCollectionRepresented {
  case github
  case travisci
  case circleci
  case bitrise
  
  static let mappedValues = [
    "github",
    "travisci",
    "circleci",
    "bitrise"
  ]
}

Now we we've made it simplifies implementing MappedValueRepresentable so let's look how to use it with Codable.

Codable Enums using a MappedEnum Type

So you've setup a MappedValueRepresentable enum, the next part is having the MappedType which in this case is String the part that's used in Codable.

This is where MappedEnum is used:

struct BuildSetup : Codable {
  let ci: MappedEnum<ContinuousIntegrationSystem>
}

Now if the String can be used in encoding and decoding the value rather than the RawType Int:

{
  "ci" : "github"
}

Next, let's take a look how we could use ContinuousIntegrationSystem in an OptionSet.

Using Enums in OptionSets with EnumSet

EnumSet allows you to interchangeably use Enum with an OptionSet. EnumSet is a Generic struct while takes any Enum type with a RawType. So we can create an OptionSet instance which uses out ContinuousIntegrationSystem:

let systems = EnumSet<ContinuousIntegrationSystem>([.travisci, .github]) 

Converting EnumSet to Enum Array

If your Enum implements CaseIterable, then you can extract the individual ContinuousIntegrationSystem enum values with .array():

enum ContinuousIntegrationSystem: Int, CaseIterable {
  case github
  case travisci
  case circleci
  case bitrise
}

let systems = EnumSet<ContinuousIntegrationSystem>([.travisci, .github]) 

print(systems.array())

Lastly, let's put all this together.

Codable EnumSet using a MappedValueRepresentable Enum

If your enum implements MappedValueRepresentable and you use it in an EnumSet, then you can allow for your OptionSet to be Codable as an Array of values rather than the cumulative rawValue:

enum ContinuousIntegrationSystem: Int, MappedValueCollectionRepresented, CaseIterable {
case github
case travisci
case circleci
case bitrise

static let mappedValues = [
  "github",
  "travisci",
  "circleci",
  "bitrise"
]
}


struct BuildSetup : Codable {
  let systems: EnumSet<ContinuousIntegrationSystem>
}

let systems = BuildSetup(systems: EnumSet<ContinuousIntegrationSystem>(values: [.travisci, .github]))

For our systems variable, our Codable data would be:

{
  "systems" : ["travisci", "github"]
}

This will make it easier for making our data human-readable instead of using the rawValue of 3.

Further Code Documentation

Documentation Here

License

This code is distributed under the MIT license. See the LICENSE file for more info.

GitHub

link
Stars: 16
Last commit: 5 days ago

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco