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
}
similarly
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?