Home

Awesome

EnumOptionSet

@EnumOptionSet is a Swift attached member macro to declare option sets using an enumeration notation.

Purpose

The built-in OptionSet declaration syntax in Swift is quite cumbersome and repetitive:

struct ShippingOptions: OptionSet {
    let rawValue: Int

    static let nextDay = Self(rawValue: 1 << 0)
    static let secondDay = Self(rawValue: 1 << 1)
    static let priority = Self(rawValue: 1 << 2)
    static let standard = Self(rawValue: 1 << 3)

    static let all: Self = [.nextDay, .secondDay, .priority, .standard]
}

The same option set can be declared with the @EnumOptionSet macro and the enum like this:

@EnumOptionSet
enum ShippingOption {
    case nextDay, secondDay, priority, standard
}

[!NOTE] The macro generates a nested Set structure that conforms to the OptionSet protocol, using Int as the default raw value type and extracting options from the names of enum cases in the order of declaration. It also generates the all property as an option composition, along with some other helper members.

Then you can create a typealias, and extend the option set with additional composite options:

typealias ShippingOptions = ShippingOption.Set
extension ShippingOptions {
    static let express: Self = [.nextDay, .secondDay]
}

[!IMPORTANT] I hope you enjoy the project and find it useful. Please bookmark it with ⭐️ and share your feedback. Thank you!

Advanced usage

The macro also supports custom raw value types and indices:

@EnumOptionSet<Int8>    // OptionSet RawValue = Int8
enum ShippingOption: Int {
    case nextDay        // Starting with index 0.       (rawValue: 1 << 0)
    case secondDay      // Incrementing by 1.           (rawValue: 1 << 1)
    case priority = 3   // Skipping index 2.            (rawValue: 1 << 3)
    case standard       // Continuing to increment.     (rawValue: 1 << 4)
}

[!TIP] The OptionSet RawValue type can also be declared as a first argument @EnumOptionSet(Int8.self). Currently, an even shorter form @EnumOptionSet(Int8) works, but this may be a bug of the Swift syntax analyzer, so use it at your own risk.

[!NOTE] Enum raw values expressed otherwise than by Int literals are ignored. The enum raw value can be declared as an arbitrary type. Conformance to CaseIterable is also not required.

Another significant advantage of the macro is that it provides additional safety checks not found in the built-in declaration. Specifically, it performs checks for duplicate indices and raw value overflow. At compile-time, the macro determines the raw value bitset size based on the type name, and at runtime, it adds an assertion using the bitWidth property of the FixedWidthInteger raw value type.

Both of checks can be disabled with the ignoreOverflow attribute flag:

@EnumOptionSet<Int8>(ignoreOverflow: true)
enum ShippingOption: Int {
    case nextDay, secondDay, priority, standard = 8 // Option bit index 8 exceeds the size of 'Int8'.
}

[!NOTE] In case of ignoring overflow, raw values that exceed the type capacity are set to zero, thereby excluding the corresponding options from the set.

Other goodies

The macro generates easy to read description and debugDescription properties:

ShippingOptions.express.description // "[nextDay, secondDay]"

ShippingOptions.express.debugDescription // "OptionSet(0b00000011)"

It also contains an OptionSet extension for accessing options as boolean flags using subscript notation:

var shippingOptions: ShippingOptions = []
shippingOptions[.standard] = true
shippingOptions[.express].toggle()
shippingOptions[.priority] = shippingOptions[.standard]
shippingOptions == .all // true

There are also multiple format checks and syntax fix suggestions. The macro code is fully covered with unit tests.

Installation

To add the package to your Xcode project, open File -> Add Package Dependencies... and search for the URL:

https://github.com/DnV1eX/EnumOptionSet.git

Then, simply import EnumOptionSet and add the @EnumOptionSet attribute before the target enum.

[!WARNING] Xcode may ask to Trust & Enable the macro on first use or after an update.

References

License

Copyright © 2024 DnV1eX. All rights reserved. Licensed under the Apache License, Version 2.0.