Calling a function for a refined protocol

Hey there,

I'm tearing my hair out with the following problem.

I want to build a system that, for a given key, will return a value for a feature flag. I want developers to be able to add feature flags for whatever they want, and the underlying system will check for a value from an array of providers, and if no provider returns a value then it will return the default value. I also want developers to create providers themselves.

Here's the protocol for the keys:

public protocol FeatureFlagKey {
    associatedtype Value

    /// The default value for the feature flag.
    static var defaultValue: Self.Value { get }
    static var externalRepresentation: String { get }

And here's the protocol for the provider:

public protocol FeatureFlagProvider {
    func value<K>(for key: K.Type) -> K.Value? where K : FeatureFlagKey

Here's the wrapper class that contains multiple providers:

public class FeatureFlagService {

    let providers: [FeatureFlagProvider]

    func value<K>(for key: K.Type) -> K.Value where K : FeatureFlagKey {
        providers.firstNonNil { provider in
            provider.value(for: key)
        } ?? key.defaultValue

Let's say that I define a feature flag key as:

struct ShinyWheelsFeatureFlagKey: FeatureFlagKey {
    static var defaultValue = true
    static var externalRepresentation = "shiny-wheels"

It turns out the one of the providers serves a value for a key as [String: Any], and the other as String. Ideally, the providers shouldn't have to know about all possible return types, so I figure I'll refine FeatureFlagKey as so:

protocol ProviderAFeatureFlagKey: FeatureFlagKey {
    func decode(_ value: [String: Any]) -> Value


protocol ProviderBFeatureFlagKey: FeatureFlagKey {
    func decode(_ value: String) -> Value

Is there any way that my feature flag provider can check for this refined protocol? If I define the following function on my provider, it won't get called – the function with where K: FeatureFlagKey always gets called.

func value<K>(for key: K.Type) -> K.Value? where K : ProviderAFeatureFlagKey {

It's also not possible to cast to ProviderAFeatureFlagKey due to the associated type requirement.

I would prefer not have the keys have to know about all possible types of provider – this will mean that it wouldn't be possible to add a new provider without changing the feature flag key protocol.

Any ideas?

1 Like