Merge pull request #17 from 0xOpenBytes/feature/StoreContent

Feature/store content
This commit is contained in:
Leif 2022-07-14 17:56:37 -06:00 committed by GitHub
commit 0de3017c8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 9 deletions

View File

@ -168,7 +168,7 @@ public extension CacheStore {
/// Creates a `ScopedCacheStore` with the given key transformation and default cache
func scope<ScopedKey: Hashable>(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>,
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>,
defaultCache: [ScopedKey: Any] = [:]
) -> CacheStore<ScopedKey> {
let scopedCacheStore = ScopedCacheStore(keyTransformation: keyTransformation)

View File

@ -1,11 +1,13 @@
import c
public typealias BiDirectionalTransformation = c.BiDirectionalTransformation
class ScopedCacheStore<Key: Hashable, ScopedKey: Hashable>: CacheStore<ScopedKey> {
weak var parentCacheStore: CacheStore<Key>?
private var keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>
private var keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>
init(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>
) {
self.keyTransformation = keyTransformation

View File

@ -0,0 +1,15 @@
import c
public extension Store {
/// Create a StoreContent for the provided content type
func content<Content: StoreContent>(
using contentType: Content.Type = Content.self
) -> Content where Content.Key == Key {
contentType.init(
store: actionlessScope(
keyTransformation: (from: { $0 }, to: { $0 }),
dependencyTransformation: { _ in () }
)
)
}
}

View File

@ -0,0 +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<Key, Void, Void>)
}

View File

@ -0,0 +1,24 @@
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<Key, Action, Dependency> { get set }
/// The content a StoreView uses when creating SwiftUI views
var content: Content { get }
init(store: Store<Key, Action, Dependency>)
}
public extension StoreView where Content.Key == Key {
var content: Content { store.content() }
}

View File

@ -139,7 +139,7 @@ public class Store<Key: Hashable, Action, Dependency>: ObservableObject, ActionH
/// Creates a `ScopedStore`
public func scope<ScopedKey: Hashable, ScopedAction, ScopedDependency>(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>,
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>,
actionHandler: StoreActionHandler<ScopedKey, ScopedAction, ScopedDependency>,
dependencyTransformation: (Dependency) -> ScopedDependency,
defaultCache: [ScopedKey: Any] = [:],
@ -182,7 +182,7 @@ public class Store<Key: Hashable, Action, Dependency>: ObservableObject, ActionH
/// Creates an Actionless `ScopedStore`
public func actionlessScope<ScopedKey: Hashable, ScopedDependency>(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>,
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>,
dependencyTransformation: (Dependency) -> ScopedDependency,
defaultCache: [ScopedKey: Any] = [:]
) -> Store<ScopedKey, Void, ScopedDependency> {
@ -225,7 +225,7 @@ public class Store<Key: Hashable, Action, Dependency>: ObservableObject, ActionH
public extension Store where Dependency == Void {
/// Creates a `ScopedStore`
func scope<ScopedKey: Hashable, ScopedAction>(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>,
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>,
actionHandler: StoreActionHandler<ScopedKey, ScopedAction, Void>,
defaultCache: [ScopedKey: Any] = [:],
actionTransformation: @escaping (ScopedAction?) -> Action? = { _ in nil }
@ -241,7 +241,7 @@ public extension Store where Dependency == Void {
/// Creates a `ScopedStore`
func actionlessScope<ScopedKey: Hashable>(
keyTransformation: c.BiDirectionalTransformation<Key?, ScopedKey?>,
keyTransformation: BiDirectionalTransformation<Key?, ScopedKey?>,
defaultCache: [ScopedKey: Any] = [:]
) -> Store<ScopedKey, Void, Void> {
actionlessScope(

View File

@ -136,6 +136,28 @@ public class TestStore<Key: Hashable, Action, Dependency> {
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<Content: StoreContent>(
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: \(error)", file, line)
return
}
}
/// Create a StoreContent for the provided content type
public func content<Content: StoreContent>(
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<Key>,

View File

@ -141,7 +141,7 @@ final class CacheStoreTests: XCTestCase {
}
let scopedCacheStore: CacheStore<ScopedCacheKey> = 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<ScopedCacheKey> = store.scope(
keyTransformation: c.transformer(
keyTransformation: (
from: { global in
switch global {
case .b: return .b