From c34006f19619b6a1650e2aca779e4fc2978fb1fa Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Mon, 13 May 2024 02:26:01 +0200 Subject: [PATCH 1/4] Enabled Throwing in `setValueSync(_:for:)` --- .../YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift | 2 +- Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift index 19895c0..8593e41 100644 --- a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift +++ b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift @@ -44,7 +44,7 @@ extension UserDefaultsStore: SynchronousMutableFeatureFlagStore { return .success(value) } - public func setValueSync(_ value: Value, for key: FeatureFlagKey) { + public func setValueSync(_ value: Value, for key: FeatureFlagKey) throws { userDefaults.set(value, forKey: key) } diff --git a/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift b/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift index 9bff3c3..a8ca7ea 100644 --- a/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift +++ b/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift @@ -91,11 +91,11 @@ final class UserDefaultsStoreTests: XCTestCase { XCTAssertEqual(userDefaults.string(forKey: "TEST_key2"), "TEST_newValue2") } - func test_setValueSync() { + func test_setValueSync() throws { userDefaults.set("TEST_value1", forKey: "TEST_key1") - store.setValueSync("TEST_newValue1", for: "TEST_key1") - store.setValueSync("TEST_newValue2", for: "TEST_key2") + try store.setValueSync("TEST_newValue1", for: "TEST_key1") + try store.setValueSync("TEST_newValue2", for: "TEST_key2") XCTAssertEqual(userDefaults.string(forKey: "TEST_key1"), "TEST_newValue1") XCTAssertEqual(userDefaults.string(forKey: "TEST_key2"), "TEST_newValue2") From f248add9f5c6dd9d87095d1555d53640054ae532 Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Mon, 13 May 2024 02:26:46 +0200 Subject: [PATCH 2/4] `UserDefaultsStore.Error` --- .../FeatureFlagResolver/Store/UserDefaultsStore.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift index 8593e41..312a4b2 100644 --- a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift +++ b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift @@ -54,4 +54,14 @@ extension UserDefaultsStore: SynchronousMutableFeatureFlagStore { } +// MARK: - Error + +extension UserDefaultsStore { + + enum Error: Swift.Error { + case optionalValuesAreNotSupported + } + +} + #endif From c83fda10f8bb0920f50521d1196fe56b8cca7aa2 Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Mon, 13 May 2024 02:29:43 +0200 Subject: [PATCH 3/4] Updated Tests --- .../Cases/UserDefaultsStoreTests.swift | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift b/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift index a8ca7ea..dc64020 100644 --- a/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift +++ b/Tests/YMFFTests/Cases/UserDefaultsStoreTests.swift @@ -81,6 +81,52 @@ final class UserDefaultsStoreTests: XCTestCase { } } + func test_value_optionals() async { + userDefaults.set("TEST_value1", forKey: "TEST_key1") + // No record for TEST_key2 + + do { + let _: String? = try await store.value(for: "TEST_key1").get() + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key1"), "TEST_value1") + } catch { + XCTFail("Unexpected error: \(error)") + } + + do { + let _: String? = try await store.value(for: "TEST_key2").get() + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertNil(userDefaults.string(forKey: "TEST_key2")) + } catch { + XCTFail("Unexpected error: \(error)") + } + } + + func test_valueSync_optionals() { + userDefaults.set("TEST_value1", forKey: "TEST_key1") + // No record for TEST_key2 + + do { + let _: String? = try store.valueSync(for: "TEST_key1").get() + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key1"), "TEST_value1") + } catch { + XCTFail("Unexpected error: \(error)") + } + + do { + let _: String? = try store.valueSync(for: "TEST_key2").get() + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertNil(userDefaults.string(forKey: "TEST_key2")) + } catch { + XCTFail("Unexpected error: \(error)") + } + } + func test_setValue() async throws { userDefaults.set("TEST_value1", forKey: "TEST_key1") @@ -101,6 +147,56 @@ final class UserDefaultsStoreTests: XCTestCase { XCTAssertEqual(userDefaults.string(forKey: "TEST_key2"), "TEST_newValue2") } + func test_setValue_optionals() async { + userDefaults.set("TEST_value1", forKey: "TEST_key1") + userDefaults.set("TEST_value2", forKey: "TEST_key2") + + do { + let optionalValue1: String? = "TEST_newValue1" + try await store.setValue(optionalValue1, for: "TEST_key1") + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key1"), "TEST_value1") + } catch { + XCTFail("Unexpected error: \(error)") + } + + do { + let optionalValue2: String? = nil + try await store.setValue(optionalValue2, for: "TEST_key2") + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key2"), "TEST_value2") + } catch { + XCTFail("Unexpected error: \(error)") + } + } + + func test_setValueSync_optionals() { + userDefaults.set("TEST_value1", forKey: "TEST_key1") + userDefaults.set("TEST_value2", forKey: "TEST_key2") + + do { + let optionalValue1: String? = "TEST_newValue1" + try store.setValueSync(optionalValue1, for: "TEST_key1") + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key1"), "TEST_value1") + } catch { + XCTFail("Unexpected error: \(error)") + } + + do { + let optionalValue2: String? = nil + try store.setValueSync(optionalValue2, for: "TEST_key2") + XCTFail("Expected an error") + } catch FeatureFlagStoreError.otherError(UserDefaultsStore.Error.optionalValuesAreNotSupported) { + XCTAssertEqual(userDefaults.string(forKey: "TEST_key2"), "TEST_value2") + } catch { + XCTFail("Unexpected error: \(error)") + } + } + func test_removeValue() async throws { userDefaults.set("TEST_value1", forKey: "TEST_key1") userDefaults.set("TEST_value2", forKey: "TEST_key2") From 860e6b88d0ecc86869f67ee2c62666c7b29c3e7a Mon Sep 17 00:00:00 2001 From: Yakov Manshin Date: Mon, 13 May 2024 02:29:53 +0200 Subject: [PATCH 4/4] Updated `UserDefaultsStore` --- .../FeatureFlagResolver/Store/UserDefaultsStore.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift index 312a4b2..71e9bf3 100644 --- a/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift +++ b/Sources/YMFF/FeatureFlagResolver/Store/UserDefaultsStore.swift @@ -39,12 +39,20 @@ final public class UserDefaultsStore { extension UserDefaultsStore: SynchronousMutableFeatureFlagStore { public func valueSync(for key: FeatureFlagKey) -> Result { + guard !(Value.self is ExpressibleByNilLiteral.Type) else { + return .failure(.otherError(Error.optionalValuesAreNotSupported)) + } + guard let anyValue = userDefaults.object(forKey: key) else { return .failure(.valueNotFound) } guard let value = anyValue as? Value else { return .failure(.typeMismatch) } return .success(value) } public func setValueSync(_ value: Value, for key: FeatureFlagKey) throws { + guard !(value is ExpressibleByNilLiteral) else { + throw FeatureFlagStoreError.otherError(Error.optionalValuesAreNotSupported) + } + userDefaults.set(value, forKey: key) }