Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down
15 changes: 15 additions & 0 deletions Sources/CacheStore/Stores/Store/Content/Store+StoreContent.swift
Original file line number Diff line number Diff line change
@@ -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 () }
)
)
}
}
7 changes: 7 additions & 0 deletions Sources/CacheStore/Stores/Store/Content/StoreContent.swift
Original file line number Diff line number Diff line change
@@ -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>)
}
24 changes: 24 additions & 0 deletions Sources/CacheStore/Stores/Store/Content/StoreView.swift
Original file line number Diff line number Diff line change
@@ -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() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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] = [:],
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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 }
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
4 changes: 2 additions & 2 deletions Tests/CacheStoreTests/CacheStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down