Merge pull request #17 from 0xOpenBytes/feature/StoreContent
Feature/store content
This commit is contained in:
commit
0de3017c8e
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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 () }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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>)
|
||||
}
|
|
@ -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() }
|
||||
}
|
|
@ -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(
|
|
@ -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>,
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue