Skip to content

Latest commit

 

History

History
153 lines (106 loc) · 5.34 KB

organize_feature_flags.md

File metadata and controls

153 lines (106 loc) · 5.34 KB

↑ DOCUMENTATION INDEX

2. Organize Feature Flags

RealFlags allows you to describe complex nested structure for flags. You can, for example, create a collection inside another collection and create a data tree for feature flags which better describe your application's requirements.

In order to define a sub-collection inside a root structure (a FlagCollectionProtocol conform object) you must use the @FlagProtocol: it allows you to identify another FlagCollectionProtocol collection.

Consider the following example:

struct AppFeatureFlags: FlagCollectionProtocol {
    // ...
    @FlagCollection(description: "Checkout Flags")
    var checkout: CheckoutFlags
    // ...
}

The checkout variable referes to another sub collection named CheckoutFlags:

struct CheckoutFlags: FlagCollectionProtocol {
    // ...
}

Once loaded you can use the dot notation to explore data in type-safe manner:

if appFlags.checkout.someProperty == "..." { ... }

A structure may contains subcollections and properties as you wish.

↑ INDEX

2.2. Nested Structures

While RealFlags leave you free to organize your feature flags the library's architecture itself encourage you to classify feature flags and group them according to your criteria.
You can stay flat or you can create nested categories.

A single struct conforms to FlagCollectionProtocol may contains both properties and other collections.

Consider the following example:

We have created a nested structure where the root is AppFeatureFlags.
We can translate the following tree in RealFlags as follow (not all properties are exposed for brevity):

struct AppFeatureFlags: FlagCollectionProtocol {
    @FlagCollection(description: "User's Related Flags")
    var user: UserFlags
    
    @FlagCollection(description: "Experimental Flags")
    var experimental: ExperimentalFlags

    @FlagCollection(description: "Checkout Flags")
    var checkout: CheckoutFlags

    @FlagCollection(description: "Color Themen")
    var colorTheme: JSONData?
}

struct UserFlags: FlagCollectionProtocol {
    @Flag(default: false, description: "Show Social Login Button")
    var showSocialLogin: Bool
    // ...
}

struct ExperimentalFlags: FlagCollectionProtocol {
    @Flag(default: false, description: "New cool cache algorithm")
    var enableFastPrecache: Bool
    
    @Flag(key: "list_layout", default: nil, description: "Layout settings (JSON)")
    var listLayoutSettings: JSONData?
}

struct CheckoutFlags: FlagCollectionProtocol {
    // ...
}

Now you have created the structure you can easily load the structure in a FlagsLoader instance and query their values with typesafe:

let appFlags = FlagsLoader(AppFeatureFlags.self, providers: [localProvider])

/// Somewhere in your code you can query a nested value
if appFlags.userFlags.showSocialLogin {
    // ...
}

↑ INDEX

2.3 Configure FlagCollection's contribution to properties keypath generation

Sometimes you may want to organize a collection of flags by creating nested structures which are transparent to the keypath generation to its inner properties.

Consider the following example with a FlagLoader with the default key configuration:

private struct TestFlags: FlagCollectionProtocol {
    @FlagCollection(description: "Group 1")
    var firstGroup: FirstGroup

    @Flag(default: false, description: "Top level test flag")
    var topLevelFlag: Bool
}

private struct FirstGroup: FlagCollectionProtocol {
    @Flag(default: false, description: "Second level test flag")
    var secondLevelFlag: Bool
}

You may want both topLevelFlag and secondLevelFlag contains the same keypath components.
By now you will get the following keypaths:

  • topLevelFlag: top-level-flag
  • secondLevelFlag: first-group/second-level-flag

While you want to ignore first-group path component.

In order to accomplish this requirement you need to specify a keyConfiguration to the FirstGroup:

@FlagCollection(keyConfiguration: .skip, description: "Group 1")
var firstGroup: FirstGroup

.skip allows you to ignore the firstGroup property to the contribution of keypath.

Allowed transformations are pretty similar to the keyConfiguration of the @Flag property wrapper. They are:

  • default: this is the default behaviour, it just uses the parent's FlagLoader's keyConfiguration setting.
  • kebabCase: refers to the style of writing in which each space is replaced by a - character. It uses the kebab case with the current property key (first-group/...)
  • snakeCase: refers to the style of writing in which each space is replaced by a _ character. It uses the snake case with the current property key (first_group/...)
  • skip: ignore the current's collection contribution to keypath generation.
  • custom(String): uses a fixed value to describe the current keypath component.

↑ INDEX