* Illustrated the use of feature flag value transformation * Updated phrasing and formatting in `README` * Updated code documentation * Updated version numbers
This commit is contained in:
parent
4205ce7dc8
commit
4704bcff97
62
README.md
62
README.md
|
@ -1,12 +1,12 @@
|
|||
# YMFF: Feature management made easy
|
||||
|
||||
Every company I worked for needed a way to manage availability of features in the apps already shipped to users. Surprisingly enough, [feature flags](https://en.wikipedia.org/wiki/Feature_toggle) (a.k.a. feature toggles a.k.a. feature switches) tend to cause a lot of struggle.
|
||||
Every company I worked for needed a way to manage availability of features in the apps already shipped to users. Surprisingly enough, [*feature flags*](https://en.wikipedia.org/wiki/Feature_toggle) (a.k.a. *feature toggles* a.k.a. *feature switches*) tend to cause a lot of struggle.
|
||||
|
||||
I aspire to change that.
|
||||
**I aspire to change that.**
|
||||
|
||||
YMFF is a nice little library that makes management of features with feature flags—and management of the feature flags themselves—a bliss, thanks to the power of Swift’s [property wrappers](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617).
|
||||
YMFF is a nice little library that makes managing features with feature flags—and managing feature flags themselves—a bliss, thanks mainly to the power of Swift’s [property wrappers](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617).
|
||||
|
||||
YMFF ships completely ready for use, right out of the box: you get everything you need to start in just a few minutes. But you can also replace nearly any component of the system with your own, customized implementation. The supplied implementation and the protocols are kept in two separate targets (YMFF and YMFFProtocols, respectively).
|
||||
YMFF ships completely ready for use, right out of the box: you get everything you need to get started in just a few minutes. But you can also replace nearly any component of the system with your own, customized implementation. The supplied implementation and the protocols are kept in two separate targets (YMFF and YMFFProtocols, respectively).
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -19,28 +19,26 @@ https://github.com/yakovmanshin/YMFF
|
|||
|
||||
You’re then prompted to select the version to install and indicate the desired update policy. I recommend starting with the latest version (it’s selected automatically), and choosing “up to next major” as the preferred update rule. Once you click Next, the package is fetched. Then select the target you’re going to use YMFF in. Click Finish, and you’re ready to go.
|
||||
|
||||
If you need to use YMFF in another Swift package, add it as a dependency:
|
||||
If you need to use YMFF in another Swift package, add it to the `Package.swift` file as a dependency:
|
||||
|
||||
```swift
|
||||
.package(url: "https://github.com/yakovmanshin/YMFF", .upToNextMajor(from: "2.0.0"))
|
||||
.package(url: "https://github.com/yakovmanshin/YMFF", .upToNextMajor(from: "3.0.0"))
|
||||
```
|
||||
|
||||
### CocoaPods
|
||||
YMFF now supports installation via [CocoaPods](https://youtu.be/iEAjvNRdZa0).
|
||||
YMFF alternatively supports installation via [CocoaPods](https://youtu.be/iEAjvNRdZa0).
|
||||
|
||||
Add the following to your Podfile:
|
||||
|
||||
```ruby
|
||||
pod 'YMFF', '~> 2.1'
|
||||
pod 'YMFF', '~> 3.0'
|
||||
```
|
||||
|
||||
## Setup
|
||||
All you need to start managing features with YMFF is at least one feature flag *store*—an object which conforms to `FeatureFlagStoreProtocol` and provides values that correspond to feature flag keys.
|
||||
|
||||
`FeatureFlagStoreProtocol` has two required methods: `containsValue(forKey:)` and `value(forKey:)`.
|
||||
All you need to start managing features with YMFF is at least one *feature flag store*—an object which conforms to `FeatureFlagStoreProtocol` and provides values that correspond to feature flag keys.
|
||||
|
||||
### Firebase Remote Config
|
||||
Firebase’s Remote Config is one of the most popular tools to manage feature flags on the back-end side. Remote Config’s `RemoteConfigValue` requires use of different methods to retrieve values of different types. Integration of YMFF with Remote Config, although doesn’t look very pretty, is quite simple.
|
||||
Firebase’s Remote Config is one of the most popular tools to manage feature flags on the server side. Remote Config’s `RemoteConfigValue` requires the use of different methods to retrieve values of different types. Integration of YMFF with Remote Config, although doesn’t look very pretty, is quite straightforward.
|
||||
|
||||
```swift
|
||||
import FirebaseRemoteConfig
|
||||
|
@ -80,9 +78,9 @@ extension RemoteConfig: FeatureFlagStoreProtocol {
|
|||
}
|
||||
```
|
||||
|
||||
Now `RemoteConfig` is a valid feature flag store.
|
||||
Now, `RemoteConfig` is a valid *feature flag store*.
|
||||
|
||||
Alternatively, you can create a custom wrapper object instead of extending `RemoteConfig`. That’s what I prefer to do in my projects for better flexibility.
|
||||
Alternatively, you can create a custom wrapper object. That’s what I tend to do in my projects to achieve greater flexibility and avoid tight coupling.
|
||||
|
||||
## Usage
|
||||
Here’s the most basic way to use YMFF:
|
||||
|
@ -111,6 +109,29 @@ enum FeatureFlags {
|
|||
@FeatureFlag("number_of_banners", default: 3, resolver: resolver)
|
||||
static var numberOfBanners
|
||||
|
||||
// Sometimes it may be convenient to transform the raw value—the one you receive from the store—
|
||||
// to the native value—the one used in your app.
|
||||
// In the following example, `MyFeatureFlagStore` stores values as strings, but the app uses an enum.
|
||||
// To switch between the types, you use a `FeatureFlagValueTransformer`.
|
||||
@FeatureFlag(
|
||||
"promo_unit_kind",
|
||||
FeatureFlagValueTransformer { string in
|
||||
PromoUnitKind(rawValue: string)
|
||||
} rawValueFromValue: { kind in
|
||||
kind.rawValue
|
||||
},
|
||||
default: .image,
|
||||
resolver: resolver
|
||||
)
|
||||
static var promoUnitKind
|
||||
|
||||
}
|
||||
|
||||
// You can create feature flags of any type.
|
||||
enum PromoUnitKind: String {
|
||||
case text
|
||||
case image
|
||||
case video
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -118,7 +139,14 @@ To the code that makes use of a feature flag, the flag acts just like the type o
|
|||
|
||||
```swift
|
||||
if FeatureFlags.promoEnabled {
|
||||
displayPromoBanners(count: FeatureFlags.numberOfBanners)
|
||||
switch FeatureFlags.promoUnitKind {
|
||||
case .text:
|
||||
displayPromoText()
|
||||
case .image:
|
||||
displayPromoBanners(count: FeatureFlags.numberOfBanners)
|
||||
case .video:
|
||||
playPromoVideo()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -161,9 +189,9 @@ Contributions are welcome!
|
|||
|
||||
Have a look at [issues](https://github.com/yakovmanshin/YMFF/issues) to see the project’s current needs. Don’t hesitate to create new issues, especially if you intend to work on them yourself.
|
||||
|
||||
If you’d like to discuss something else regarding YMFF (or not), contact [me](https://github.com/yakovmanshin) via email (the address is in the profile).
|
||||
If you’d like to discuss something else, contact [me](https://github.com/yakovmanshin) via email (the address is in the profile).
|
||||
|
||||
## License and Copyright
|
||||
YMFF is available under the terms of the Apache License, version 2.0. See the [LICENSE file](https://github.com/yakovmanshin/YMFF/blob/main/LICENSE) for details.
|
||||
|
||||
© 2020–2021 Yakov Manshin
|
||||
© 2020–2022 Yakov Manshin
|
||||
|
|
|
@ -15,6 +15,10 @@ public struct FeatureFlagValueTransformer<RawValue, Value> {
|
|||
let rawValueFromValue: (Value) -> RawValue
|
||||
|
||||
/// Creates a new instance of transformer using the specified transformation closures.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - valueFromRawValue: *Required.* The closure which attempts to transform a raw value into a value.
|
||||
/// - rawValueFromValue: *Required.* The closure which transforms a value into a raw value.
|
||||
public init(
|
||||
valueFromRawValue: @escaping (RawValue) -> Value?,
|
||||
rawValueFromValue: @escaping (Value) -> RawValue
|
||||
|
@ -29,6 +33,7 @@ public struct FeatureFlagValueTransformer<RawValue, Value> {
|
|||
|
||||
extension FeatureFlagValueTransformer where RawValue == Value {
|
||||
|
||||
/// The transformer which converts between values of the same type.
|
||||
static var identity: FeatureFlagValueTransformer {
|
||||
.init(valueFromRawValue: { $0 }, rawValueFromValue: { $0 })
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ final public class FeatureFlagResolverConfiguration {
|
|||
|
||||
public var stores: [FeatureFlagStore]
|
||||
|
||||
/// Initializes a new configuration with the given array of feature flag stores.
|
||||
public init(stores: [FeatureFlagStore]) {
|
||||
self.stores = stores
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue