From 25779870dd31f25abc7695337870cd8437af9fc2 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 12:51:15 -0600 Subject: [PATCH 1/7] Add StoreContent and StoreView --- .../Stores/{ => CacheStore}/CacheStore.swift | 0 .../ScopedCacheStore.swift | 0 .../ContentStore/Store+StoreContent.swift | 14 ++++++++++++++ .../Stores/ContentStore/StoreContent.swift | 7 +++++++ .../Stores/ContentStore/StoreView.swift | 17 +++++++++++++++++ .../{ScopedStores => Store}/ScopedStore.swift | 0 .../CacheStore/Stores/{ => Store}/Store.swift | 0 .../Stores/{ => Store}/TestStore.swift | 0 8 files changed, 38 insertions(+) rename Sources/CacheStore/Stores/{ => CacheStore}/CacheStore.swift (100%) rename Sources/CacheStore/Stores/{ScopedStores => CacheStore}/ScopedCacheStore.swift (100%) create mode 100644 Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift create mode 100644 Sources/CacheStore/Stores/ContentStore/StoreContent.swift create mode 100644 Sources/CacheStore/Stores/ContentStore/StoreView.swift rename Sources/CacheStore/Stores/{ScopedStores => Store}/ScopedStore.swift (100%) rename Sources/CacheStore/Stores/{ => Store}/Store.swift (100%) rename Sources/CacheStore/Stores/{ => Store}/TestStore.swift (100%) diff --git a/Sources/CacheStore/Stores/CacheStore.swift b/Sources/CacheStore/Stores/CacheStore/CacheStore.swift similarity index 100% rename from Sources/CacheStore/Stores/CacheStore.swift rename to Sources/CacheStore/Stores/CacheStore/CacheStore.swift diff --git a/Sources/CacheStore/Stores/ScopedStores/ScopedCacheStore.swift b/Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift similarity index 100% rename from Sources/CacheStore/Stores/ScopedStores/ScopedCacheStore.swift rename to Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift diff --git a/Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift b/Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift new file mode 100644 index 0000000..1903dcf --- /dev/null +++ b/Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift @@ -0,0 +1,14 @@ +import c + +public extension Store { + func content( + using contentType: Content.Type = Content.self + ) -> Content where Content.Key == Key { + contentType.init( + store: actionlessScope( + keyTransformation: c.transformer(from: { $0 }, to: { $0 }), + dependencyTransformation: { _ in () } + ) + ) + } +} diff --git a/Sources/CacheStore/Stores/ContentStore/StoreContent.swift b/Sources/CacheStore/Stores/ContentStore/StoreContent.swift new file mode 100644 index 0000000..49f75d2 --- /dev/null +++ b/Sources/CacheStore/Stores/ContentStore/StoreContent.swift @@ -0,0 +1,7 @@ +import c + +public protocol StoreContent { + associatedtype Key: Hashable + + init(store: Store) +} diff --git a/Sources/CacheStore/Stores/ContentStore/StoreView.swift b/Sources/CacheStore/Stores/ContentStore/StoreView.swift new file mode 100644 index 0000000..4773cc0 --- /dev/null +++ b/Sources/CacheStore/Stores/ContentStore/StoreView.swift @@ -0,0 +1,17 @@ +import SwiftUI + +public protocol StoreView: View { + associatedtype Key: Hashable + associatedtype Action + associatedtype Dependency + associatedtype Content: StoreContent + + var store: Store { get set } + var content: Content { get } + + init(store: Store) +} + +public extension StoreView where Content.Key == Key { + var content: Content { store.content() } +} diff --git a/Sources/CacheStore/Stores/ScopedStores/ScopedStore.swift b/Sources/CacheStore/Stores/Store/ScopedStore.swift similarity index 100% rename from Sources/CacheStore/Stores/ScopedStores/ScopedStore.swift rename to Sources/CacheStore/Stores/Store/ScopedStore.swift diff --git a/Sources/CacheStore/Stores/Store.swift b/Sources/CacheStore/Stores/Store/Store.swift similarity index 100% rename from Sources/CacheStore/Stores/Store.swift rename to Sources/CacheStore/Stores/Store/Store.swift diff --git a/Sources/CacheStore/Stores/TestStore.swift b/Sources/CacheStore/Stores/Store/TestStore.swift similarity index 100% rename from Sources/CacheStore/Stores/TestStore.swift rename to Sources/CacheStore/Stores/Store/TestStore.swift From edced34fbe7c0a26dfa55c0e8c0df2f17bd69e36 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 13:20:46 -0600 Subject: [PATCH 2/7] Update file structure and remove import c --- .../{ContentStore => Store/Content}/Store+StoreContent.swift | 0 .../Stores/{ContentStore => Store/Content}/StoreContent.swift | 2 -- .../Stores/{ContentStore => Store/Content}/StoreView.swift | 0 3 files changed, 2 deletions(-) rename Sources/CacheStore/Stores/{ContentStore => Store/Content}/Store+StoreContent.swift (100%) rename Sources/CacheStore/Stores/{ContentStore => Store/Content}/StoreContent.swift (91%) rename Sources/CacheStore/Stores/{ContentStore => Store/Content}/StoreView.swift (100%) diff --git a/Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift b/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift similarity index 100% rename from Sources/CacheStore/Stores/ContentStore/Store+StoreContent.swift rename to Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift diff --git a/Sources/CacheStore/Stores/ContentStore/StoreContent.swift b/Sources/CacheStore/Stores/Store/Content/StoreContent.swift similarity index 91% rename from Sources/CacheStore/Stores/ContentStore/StoreContent.swift rename to Sources/CacheStore/Stores/Store/Content/StoreContent.swift index 49f75d2..fd33763 100644 --- a/Sources/CacheStore/Stores/ContentStore/StoreContent.swift +++ b/Sources/CacheStore/Stores/Store/Content/StoreContent.swift @@ -1,5 +1,3 @@ -import c - public protocol StoreContent { associatedtype Key: Hashable diff --git a/Sources/CacheStore/Stores/ContentStore/StoreView.swift b/Sources/CacheStore/Stores/Store/Content/StoreView.swift similarity index 100% rename from Sources/CacheStore/Stores/ContentStore/StoreView.swift rename to Sources/CacheStore/Stores/Store/Content/StoreView.swift From b07e713ecb8be61be441fbb1e90d19900dc5e256 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 13:34:36 -0600 Subject: [PATCH 3/7] Add some documentation and update TestStore --- .../Stores/Store/Content/Store+StoreContent.swift | 1 + Sources/CacheStore/Stores/Store/Content/StoreContent.swift | 2 ++ Sources/CacheStore/Stores/Store/Content/StoreView.swift | 7 +++++++ Sources/CacheStore/Stores/Store/TestStore.swift | 7 +++++++ 4 files changed, 17 insertions(+) diff --git a/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift b/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift index 1903dcf..b6f2bc7 100644 --- a/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift +++ b/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift @@ -1,6 +1,7 @@ import c public extension Store { + /// Create a StoreContent for the provided content type func content( using contentType: Content.Type = Content.self ) -> Content where Content.Key == Key { diff --git a/Sources/CacheStore/Stores/Store/Content/StoreContent.swift b/Sources/CacheStore/Stores/Store/Content/StoreContent.swift index fd33763..b9d56b9 100644 --- a/Sources/CacheStore/Stores/Store/Content/StoreContent.swift +++ b/Sources/CacheStore/Stores/Store/Content/StoreContent.swift @@ -1,5 +1,7 @@ +/// The content a StoreView uses when creating SwiftUI views public protocol StoreContent { associatedtype Key: Hashable + /// Creates the content from an actionless store that has a Void dependency init(store: Store) } diff --git a/Sources/CacheStore/Stores/Store/Content/StoreView.swift b/Sources/CacheStore/Stores/Store/Content/StoreView.swift index 4773cc0..d17363a 100644 --- a/Sources/CacheStore/Stores/Store/Content/StoreView.swift +++ b/Sources/CacheStore/Stores/Store/Content/StoreView.swift @@ -1,12 +1,19 @@ import SwiftUI +/// SwiftUI View that uses a Store and StoreContent public protocol StoreView: View { + /// Key for the Store associatedtype Key: Hashable + /// Action for the Store associatedtype Action + /// Dependency for the Store associatedtype Dependency + /// The content the View cares about and uses associatedtype Content: StoreContent + /// An `ObservableObject` that uses actions to modify the state which is a `CacheStore` var store: Store { get set } + /// The content a StoreView uses when creating SwiftUI views var content: Content { get } init(store: Store) diff --git a/Sources/CacheStore/Stores/Store/TestStore.swift b/Sources/CacheStore/Stores/Store/TestStore.swift index 46765e8..36f2153 100644 --- a/Sources/CacheStore/Stores/Store/TestStore.swift +++ b/Sources/CacheStore/Stores/Store/TestStore.swift @@ -136,6 +136,13 @@ public class TestStore { send(nextAction, file: file, line: line, expecting: expecting) } + /// Create a StoreContent for the provided content type + func content( + using contentType: Content.Type = Content.self + ) -> Content where Content.Key == Key { + store.content(using: contentType) + } + /// Checks to make sure the cache has the required keys, otherwise it will fail func require( keys: Set, From b4e45015d6d03d1eca9605321cbcce8f7fb9a37d Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 13:35:19 -0600 Subject: [PATCH 4/7] Add public access type --- Sources/CacheStore/Stores/Store/TestStore.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CacheStore/Stores/Store/TestStore.swift b/Sources/CacheStore/Stores/Store/TestStore.swift index 36f2153..9f8a7c3 100644 --- a/Sources/CacheStore/Stores/Store/TestStore.swift +++ b/Sources/CacheStore/Stores/Store/TestStore.swift @@ -137,7 +137,7 @@ public class TestStore { } /// Create a StoreContent for the provided content type - func content( + public func content( using contentType: Content.Type = Content.self ) -> Content where Content.Key == Key { store.content(using: contentType) From c6c0d788012bce280ec13ba5b4a1b5e3e5b66657 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 13:43:27 -0600 Subject: [PATCH 5/7] Add content expecting for test assertions --- Sources/CacheStore/Stores/Store/TestStore.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sources/CacheStore/Stores/Store/TestStore.swift b/Sources/CacheStore/Stores/Store/TestStore.swift index 9f8a7c3..59df78f 100644 --- a/Sources/CacheStore/Stores/Store/TestStore.swift +++ b/Sources/CacheStore/Stores/Store/TestStore.swift @@ -136,6 +136,21 @@ public class TestStore { send(nextAction, file: file, line: line, expecting: expecting) } + /// Create a StoreContent for the provided content type and make assertions in the expecting closure about the content + public func content( + using contentType: Content.Type = Content.self, + expecting: @escaping (Content) throws -> Void, + file: StaticString = #filePath, + line: UInt = #line + ) where Content.Key == Key { + do { + try expecting(content(using: contentType)) + } catch { + TestStoreFailure.handler("❌ Expectation failed", file, line) + return + } + } + /// Create a StoreContent for the provided content type public func content( using contentType: Content.Type = Content.self From c2ae9c3814c0685d984498661f8b31036ffcc055 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Fri, 8 Jul 2022 13:49:28 -0600 Subject: [PATCH 6/7] Pass the error to the failure --- Sources/CacheStore/Stores/Store/TestStore.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CacheStore/Stores/Store/TestStore.swift b/Sources/CacheStore/Stores/Store/TestStore.swift index 59df78f..d4fe7d7 100644 --- a/Sources/CacheStore/Stores/Store/TestStore.swift +++ b/Sources/CacheStore/Stores/Store/TestStore.swift @@ -146,7 +146,7 @@ public class TestStore { do { try expecting(content(using: contentType)) } catch { - TestStoreFailure.handler("❌ Expectation failed", file, line) + TestStoreFailure.handler("❌ Expectation failed: \(error)", file, line) return } } From 208e737efbc4cae3ddf1d8f4f4a3807d3e72fa39 Mon Sep 17 00:00:00 2001 From: 0xLeif Date: Wed, 13 Jul 2022 19:29:04 -0600 Subject: [PATCH 7/7] Add typealias for BiDirectionalTransformation --- Sources/CacheStore/Stores/CacheStore/CacheStore.swift | 2 +- .../CacheStore/Stores/CacheStore/ScopedCacheStore.swift | 6 ++++-- .../Stores/Store/Content/Store+StoreContent.swift | 2 +- Sources/CacheStore/Stores/Store/Store.swift | 8 ++++---- Tests/CacheStoreTests/CacheStoreTests.swift | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Sources/CacheStore/Stores/CacheStore/CacheStore.swift b/Sources/CacheStore/Stores/CacheStore/CacheStore.swift index c62efd0..243401c 100644 --- a/Sources/CacheStore/Stores/CacheStore/CacheStore.swift +++ b/Sources/CacheStore/Stores/CacheStore/CacheStore.swift @@ -168,7 +168,7 @@ public extension CacheStore { /// Creates a `ScopedCacheStore` with the given key transformation and default cache func scope( - keyTransformation: c.BiDirectionalTransformation, + keyTransformation: BiDirectionalTransformation, defaultCache: [ScopedKey: Any] = [:] ) -> CacheStore { let scopedCacheStore = ScopedCacheStore(keyTransformation: keyTransformation) diff --git a/Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift b/Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift index af68dae..492ccb1 100644 --- a/Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift +++ b/Sources/CacheStore/Stores/CacheStore/ScopedCacheStore.swift @@ -1,11 +1,13 @@ import c +public typealias BiDirectionalTransformation = c.BiDirectionalTransformation + class ScopedCacheStore: CacheStore { weak var parentCacheStore: CacheStore? - private var keyTransformation: c.BiDirectionalTransformation + private var keyTransformation: BiDirectionalTransformation init( - keyTransformation: c.BiDirectionalTransformation + keyTransformation: BiDirectionalTransformation ) { self.keyTransformation = keyTransformation diff --git a/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift b/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift index b6f2bc7..372d9b2 100644 --- a/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift +++ b/Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift @@ -7,7 +7,7 @@ public extension Store { ) -> Content where Content.Key == Key { contentType.init( store: actionlessScope( - keyTransformation: c.transformer(from: { $0 }, to: { $0 }), + keyTransformation: (from: { $0 }, to: { $0 }), dependencyTransformation: { _ in () } ) ) diff --git a/Sources/CacheStore/Stores/Store/Store.swift b/Sources/CacheStore/Stores/Store/Store.swift index 462f76c..ea72ee6 100644 --- a/Sources/CacheStore/Stores/Store/Store.swift +++ b/Sources/CacheStore/Stores/Store/Store.swift @@ -139,7 +139,7 @@ public class Store: ObservableObject, ActionH /// Creates a `ScopedStore` public func scope( - keyTransformation: c.BiDirectionalTransformation, + keyTransformation: BiDirectionalTransformation, actionHandler: StoreActionHandler, dependencyTransformation: (Dependency) -> ScopedDependency, defaultCache: [ScopedKey: Any] = [:], @@ -182,7 +182,7 @@ public class Store: ObservableObject, ActionH /// Creates an Actionless `ScopedStore` public func actionlessScope( - keyTransformation: c.BiDirectionalTransformation, + keyTransformation: BiDirectionalTransformation, dependencyTransformation: (Dependency) -> ScopedDependency, defaultCache: [ScopedKey: Any] = [:] ) -> Store { @@ -225,7 +225,7 @@ public class Store: ObservableObject, ActionH public extension Store where Dependency == Void { /// Creates a `ScopedStore` func scope( - keyTransformation: c.BiDirectionalTransformation, + keyTransformation: BiDirectionalTransformation, actionHandler: StoreActionHandler, defaultCache: [ScopedKey: Any] = [:], actionTransformation: @escaping (ScopedAction?) -> Action? = { _ in nil } @@ -241,7 +241,7 @@ public extension Store where Dependency == Void { /// Creates a `ScopedStore` func actionlessScope( - keyTransformation: c.BiDirectionalTransformation, + keyTransformation: BiDirectionalTransformation, defaultCache: [ScopedKey: Any] = [:] ) -> Store { actionlessScope( diff --git a/Tests/CacheStoreTests/CacheStoreTests.swift b/Tests/CacheStoreTests/CacheStoreTests.swift index 1bba498..83a369b 100644 --- a/Tests/CacheStoreTests/CacheStoreTests.swift +++ b/Tests/CacheStoreTests/CacheStoreTests.swift @@ -141,7 +141,7 @@ final class CacheStoreTests: XCTestCase { } let scopedCacheStore: CacheStore = store.scope( - keyTransformation: c.transformer( + keyTransformation: ( from: { global in switch global { case .b: return .b @@ -190,7 +190,7 @@ final class CacheStoreTests: XCTestCase { let storeOldScopeValue: String = scopedCacheStore.resolve(.b) let newlyScopedCacheStore: CacheStore = store.scope( - keyTransformation: c.transformer( + keyTransformation: ( from: { global in switch global { case .b: return .b