From 978e96e7c9bb020fb6710cdb969d12f081e444ea Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Thu, 24 Dec 2020 23:31:20 +0300 Subject: [PATCH 1/7] [#27] FeatureFlag Projected Value (#28) * Added `projectedValue` property on `FeatureFlag` that returns the instance of `FeatureFlag` * Switched `FeatureFlag` from struct to class * Added tests to check types of values returned by the `projectedValue` property --- Sources/YMFF/FeatureFlag/FeatureFlag.swift | 6 ++++- Tests/YMFFTests/FeatureFlagTests.swift | 30 ++++++++++++++++++++++ Tests/YMFFTests/XCTestManifests.swift | 5 ++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Sources/YMFF/FeatureFlag/FeatureFlag.swift b/Sources/YMFF/FeatureFlag/FeatureFlag.swift index e94e481..102ebc9 100644 --- a/Sources/YMFF/FeatureFlag/FeatureFlag.swift +++ b/Sources/YMFF/FeatureFlag/FeatureFlag.swift @@ -8,7 +8,7 @@ /// An object that facilitates access to feature flag values. @propertyWrapper -public struct FeatureFlag { +final public class FeatureFlag { // MARK: Properties @@ -45,4 +45,8 @@ public struct FeatureFlag { } } + // MARK: Projected Value + + public var projectedValue: FeatureFlag { self } + } diff --git a/Tests/YMFFTests/FeatureFlagTests.swift b/Tests/YMFFTests/FeatureFlagTests.swift index a950b86..c15d718 100644 --- a/Tests/YMFFTests/FeatureFlagTests.swift +++ b/Tests/YMFFTests/FeatureFlagTests.swift @@ -79,3 +79,33 @@ extension FeatureFlagTests { } } + +// MARK: - Projected Value Tests + +extension FeatureFlagTests { + + func testBoolProjectedValue() { + XCTAssertTrue(value($boolFeatureFlag, isOfType: FeatureFlag.self)) + } + + func testIntProjectedValue() { + XCTAssertTrue(value($intFeatureFlag, isOfType: FeatureFlag.self)) + } + + func testStringProjectedValue() { + XCTAssertTrue(value($stringFeatureFlag, isOfType: FeatureFlag.self)) + } + + func testOptionalIntProjectedValue() { + XCTAssertTrue(value($optionalIntFeatureFlag, isOfType: FeatureFlag.self)) + } + + func testNonexistentIntProjectedValue() { + XCTAssertTrue(value($nonexistentIntFeatureFlag, isOfType: FeatureFlag.self)) + } + + private func value(_ value: Any, isOfType type: T.Type) -> Bool { + value is T + } + +} diff --git a/Tests/YMFFTests/XCTestManifests.swift b/Tests/YMFFTests/XCTestManifests.swift index 5acaf0f..c6a0e00 100644 --- a/Tests/YMFFTests/XCTestManifests.swift +++ b/Tests/YMFFTests/XCTestManifests.swift @@ -28,11 +28,16 @@ extension FeatureFlagTests { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__FeatureFlagTests = [ + ("testBoolProjectedValue", testBoolProjectedValue), ("testBoolWrappedValue", testBoolWrappedValue), + ("testIntProjectedValue", testIntProjectedValue), ("testIntWrappedValue", testIntWrappedValue), + ("testNonexistentIntProjectedValue", testNonexistentIntProjectedValue), ("testNonexistentIntWrappedValue", testNonexistentIntWrappedValue), ("testNonexistentWrappedValueOverride", testNonexistentWrappedValueOverride), + ("testOptionalIntProjectedValue", testOptionalIntProjectedValue), ("testOptionalIntValue", testOptionalIntValue), + ("testStringProjectedValue", testStringProjectedValue), ("testStringWrappedValue", testStringWrappedValue), ("testWrappedValueOverride", testWrappedValueOverride), ] From ec65aa8474c8811d22f88dd96536f96315725d60 Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Thu, 24 Dec 2020 23:58:22 +0300 Subject: [PATCH 2/7] [#26] Runtime Override Removal (#29) * Added `removeRuntimeOverride()` method on `FeatureFlag` * Updated tests --- Sources/YMFF/FeatureFlag/FeatureFlag.swift | 7 +++++++ Tests/YMFFTests/FeatureFlagTests.swift | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Sources/YMFF/FeatureFlag/FeatureFlag.swift b/Sources/YMFF/FeatureFlag/FeatureFlag.swift index 102ebc9..e69efb6 100644 --- a/Sources/YMFF/FeatureFlag/FeatureFlag.swift +++ b/Sources/YMFF/FeatureFlag/FeatureFlag.swift @@ -49,4 +49,11 @@ final public class FeatureFlag { public var projectedValue: FeatureFlag { self } + // MARK: Runtime Overrides + + /// Removes the feature flag value that overrides persistent values in runtime. + public func removeRuntimeOverride() { + resolver.removeRuntimeOverride(for: key) + } + } diff --git a/Tests/YMFFTests/FeatureFlagTests.swift b/Tests/YMFFTests/FeatureFlagTests.swift index c15d718..70fd678 100644 --- a/Tests/YMFFTests/FeatureFlagTests.swift +++ b/Tests/YMFFTests/FeatureFlagTests.swift @@ -68,6 +68,10 @@ extension FeatureFlagTests { overrideFlag = 789 XCTAssertEqual(overrideFlag, 789) + + $overrideFlag.removeRuntimeOverride() + + XCTAssertEqual(overrideFlag, 456) } func testNonexistentWrappedValueOverride() { @@ -76,6 +80,10 @@ extension FeatureFlagTests { nonexistentOverrideFlag = 789 XCTAssertEqual(nonexistentOverrideFlag, 789) + + $nonexistentOverrideFlag.removeRuntimeOverride() + + XCTAssertEqual(nonexistentOverrideFlag, 999) } } From 2e2fd8be602d8e9d124f1aa08fd7665e2af83b2c Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Fri, 25 Dec 2020 13:29:09 +0300 Subject: [PATCH 3/7] [#25] Added Documentation for Runtime Overriding (#30) --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 8b1b7c4..c67b3e1 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,26 @@ if FeatureFlags.promoEnabled { } ``` +### Overriding Values in Runtime + +YMFF lets you override feature flag values in runtime. One particular use case for changing values locally is when you need to test a particular feature covered with the feature flag, and you cannot or don’t want to modify the back-end configuration. + +**Runtime overrides work within a single session. Once you restart the app, the override values are erased.** + +Overriding a feature flag value in runtime is as simple as assigning the new value to the flag. + +```swift +FeatureFlags.promoEnabled = true +``` + +To remove the override and revert to using values from persistent stores, you can restart the app or call the `removeRuntimeOverride()` method on `FeatureFlag`’s *projected value* (i.e. the `FeatureFlag` instance itself, as opposed to its *wrapped value*). + +```swift +// Here `FeatureFlags.$promoEnabled` has the type `FeatureFlag`, +// while `FeatureFlags.promoEnabled` is of type `Bool`. +FeatureFlags.$promoEnabled.removeRuntimeOverride() +``` + You can browse the source files to learn more about the options available to you. An extended documentation is coming later. ## Contributing From 260bbb74fe735d538c2ec65f7414beb94a577b5a Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Fri, 25 Dec 2020 22:06:33 +0300 Subject: [PATCH 4/7] Access Control Updates in FeatureFlag * Switched `key` and `defaultValue` from private to public --- Sources/YMFF/FeatureFlag/FeatureFlag.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/YMFF/FeatureFlag/FeatureFlag.swift b/Sources/YMFF/FeatureFlag/FeatureFlag.swift index e69efb6..fe55d66 100644 --- a/Sources/YMFF/FeatureFlag/FeatureFlag.swift +++ b/Sources/YMFF/FeatureFlag/FeatureFlag.swift @@ -12,8 +12,9 @@ final public class FeatureFlag { // MARK: Properties - private let key: FeatureFlagKey - private let defaultValue: Value + public let key: FeatureFlagKey + public let defaultValue: Value + private let resolver: FeatureFlagResolverProtocol // MARK: Initializers From 4fab7cce17b984c37f931bc98be466fe05a39fae Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Fri, 25 Dec 2020 22:08:05 +0300 Subject: [PATCH 5/7] Updated FeatureFlag Documentation * Added missing docs * Fixed outdated ones --- Sources/YMFF/FeatureFlag/FeatureFlag.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/YMFF/FeatureFlag/FeatureFlag.swift b/Sources/YMFF/FeatureFlag/FeatureFlag.swift index fe55d66..0ba8e62 100644 --- a/Sources/YMFF/FeatureFlag/FeatureFlag.swift +++ b/Sources/YMFF/FeatureFlag/FeatureFlag.swift @@ -12,7 +12,10 @@ final public class FeatureFlag { // MARK: Properties + /// The key used to retrieve feature flag values. public let key: FeatureFlagKey + + /// The fallback value returned when no store is able to provide the real one. public let defaultValue: Value private let resolver: FeatureFlagResolverProtocol @@ -22,9 +25,9 @@ final public class FeatureFlag { /// Creates a new `FeatureFlag`. /// /// - Parameters: - /// - key: *Required.* The string used to address feature flag values in both the local and remote stores. - /// - defaultValue: *Required.* The value returned in case both the local and remote stores failed to provide values by the key. - /// - resolver: *Required.* The resolver object used to retrieve values from the stores. + /// - key: *Required.* The key used to address feature flag values in stores. + /// - defaultValue: *Required.* The value returned in case all stores fail to provide a value. + /// - resolver: *Required.* The resolver object used to retrieve values from stores. public init( _ key: FeatureFlagKey, default defaultValue: Value, @@ -48,6 +51,7 @@ final public class FeatureFlag { // MARK: Projected Value + /// The object returned when referencing the feature flag with a dollar sign (`$`). public var projectedValue: FeatureFlag { self } // MARK: Runtime Overrides From 6d832628d32da5d78b46095320b2f6b748b3c4e1 Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Fri, 25 Dec 2020 22:58:40 +0300 Subject: [PATCH 6/7] Updated .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index db92fb6..6cb6e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store /.build -/docs/docsets/ /.swiftpm /Packages /*.xcodeproj From e0358686211924588dbf028702c02e5410a0539e Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Fri, 25 Dec 2020 23:22:15 +0300 Subject: [PATCH 7/7] Added Jazzy Configuration --- .jazzy.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .jazzy.yaml diff --git a/.jazzy.yaml b/.jazzy.yaml new file mode 100644 index 0000000..ae28ab6 --- /dev/null +++ b/.jazzy.yaml @@ -0,0 +1,30 @@ +author: Yakov Manshin +author_url: https://yakovmanshin.com/ +clean: true +copyright: © 2020 [Yakov Manshin](https://yakovmanshin.com/). Available under [Apache License v2](https://github.com/yakovmanshin/YMFF/blob/main/LICENSE). +custom_categories: + - name: FeatureFlag + children: + - FeatureFlag + - FeatureFlagKey + - name: Resolver + children: + - FeatureFlagResolverProtocol + - FeatureFlagResolver + - FeatureFlagResolverConfigurationProtocol + - FeatureFlagResolverConfiguration + - FeatureFlagResolverError + - name: Stores + children: + - FeatureFlagStoreProtocol + - MutableFeatureFlagStoreProtocol + - FeatureFlagStore + - TransparentFeatureFlagStore + - RuntimeOverridesStore +disable_search: true +github_file_prefix: https://github.com/yakovmanshin/YMFF/tree/master +github_url: https://github.com/yakovmanshin/YMFF +hide_documentation_coverage: true +module: YMFF +title: YMFF Docs +undocumented_text: Documentation Coming Soon…