Skip to content

Instantly share code, notes, and snippets.

@hannesoid
Last active November 9, 2021 11:27
Show Gist options
  • Save hannesoid/b97d4d7d5e31296639bc0980c3f132ad to your computer and use it in GitHub Desktop.
Save hannesoid/b97d4d7d5e31296639bc0980c3f132ad to your computer and use it in GitHub Desktop.

OptionSet vs Set

For a simple element type, pros & cons

Example UseCase

struct Model {
   let id: String
   let title: String}

func changes(from lhs: Model, to rhs: Model) -> Parts {  }

let changedParts = changes(from: , to: )

// things of interest
changedParts.contains(.id)  // reload all
changedParts.isSubset(of: [.title]) // just update the text view
changedParts.isEmpty // ignore

Declaration

// OptionSet

struct Parts: OptionSet { // one single type

    let rawValue: Int

    public static let id: Self = Parts1(rawValue: 1 << 0)
    public static let title: Self = Parts1(rawValue: 1 << 1)

    init(rawValue: Int) {
        self.rawValue = rawValue
    }
}

// Set<Enum>

typealias Parts = Set<Part> // two types needed, inner Part, and outer Set

enum Part { // nice to read
    case id
    case title
}

Usage

func doSomething(with parts: Parts) {  }

// OptionSet
doSomething(with: .id) // less brackets
doSomething(with: .title)
doSomething(with: [])
doSomething(with: [.id])
doSomething(with: [.title])
doSomething(with: [.id, .title])
Parts.id.formUnion(.title) // compound and set type are the same, this is simpler than Set<…>

// Set<Enum>
doSomething(with: [])
doSomething(with: [.id])
doSomething(with: [.title])
doSomething(with: [.id, .title])
[Part.id].formUnion([.title])

Otherwise they both behave like Set.

Performance

Since OptionSet is based on a single Int and bit masks, performance should be better. (depends a lot on usage if relevant)

Automatic String Description

let id: Parts = .id
let mixed: Parts = 

// OptionSet
print(Parts.id) // "Parts(rawValue: 1)"
print([.id, .title] as Parts) // "Parts(rawValue: 3)" <- useless

// Set<Enum>
print(Part.id) // "id"
print([.id, .title]) // "[Part.id, Part.title]" <- nicer
print([.id, .title]) // "[MyModule.Scope.Part.id, MyModule.Scope.Part.title]" can get long when from a different module and nested

Iterate through cases

// OptionSet

extension Parts {
  static var allCases: [.id, .title] // Need to add custom code that lists all options
}

// Set<Enum>

enum Parts: CaseIterable {} // `.allCases` is automatically generated

Compatibility

  • Set cannot be bridged to ObjC NSSet, because Enums are not objects.
  • (NS)OptionSet can be declared in ObjC instead of Swift aswell and then used from both, or the rawValue can be passed around

Codable

  • Codable if enum is Codable. Set will encode as Array. If used for persistence, need to pay attention that enum rawValues are stable.
  • OptionSet will encode a single rawValue. If used for persistence, need to pay attention that rawValues are stable.

Extensibility

  • OptionSet cannot gain sub-detail per case
  • In Set, the enum can in the future receive associated Values, but this changes a lot (no longer CaseIterable etc) and is probably a different use case
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment