diff --git a/Sources/Store/Action.swift b/Sources/Store/Action.swift index 3d9f744..72d1eba 100644 --- a/Sources/Store/Action.swift +++ b/Sources/Store/Action.swift @@ -21,3 +21,7 @@ public extension ActionProtocol { } public final class UnspecifiedStore: Store { } + + + + diff --git a/Sources/Store/Store.swift b/Sources/Store/Store.swift index 9b570a1..6c54315 100644 --- a/Sources/Store/Store.swift +++ b/Sources/Store/Store.swift @@ -24,6 +24,9 @@ public protocol AnyStoreProtocol: class { /// Notify all of the registered middleware services. /// - note: See `MiddlewareType.onTransactionStateChange`. func notifyMiddleware(transaction: TransactionProtocol) + + /// Recursively traverse the parents until it founds one that matches the specified model type. + func parent(type: T.Type) -> Store? } public protocol StoreProtocol: AnyStoreProtocol { @@ -115,7 +118,9 @@ open class Store: StoreProtocol, ObservableObject { // MARK: Children stores /// Creates a store for a subtree of the wrapped model. - /// Similar to Redux `combineStores`. + /// As logic grows could be convient to split store into smaller one, still using the same + /// root model. + /// - note: Similar to Redux `combineStores`. public func makeChildStore( keyPath: WritableKeyPath, create: (M_1) -> Store = { Store(model: $0) } @@ -129,6 +134,14 @@ open class Store: StoreProtocol, ObservableObject { } return childStore } + + /// Recursively traverse the parents until it founds one that matches the specified model type. + public func parent(type: T.Type) -> Store? { + if let parent = parent as? Store { + return parent + } + return parent?.parent(type: type) + } // MARK: Executing transactions diff --git a/Sources/Store/TemplateActions.swift b/Sources/Store/TemplateActions.swift new file mode 100644 index 0000000..f9ffa6f --- /dev/null +++ b/Sources/Store/TemplateActions.swift @@ -0,0 +1,48 @@ +import Foundation +import os.log + +// MARK: - Template Actions + +/// General-purpose actions that can be applied to any store. +public struct TemplateAction { + + public struct AssignKeyPath: ActionProtocol { + public let keyPath: WritableKeyPath + public let value: V + + public func reduce(context: TransactionContext, Self>) { + defer { context.fulfill() } + context.reduceModel { model in model[keyPath: keyPath] = value } + } + } + + public struct AssignOptionalKeyPath: ActionProtocol { + public let keyPath: WritableKeyPath + public let value: V? + + public func reduce(context: TransactionContext, Self>) { + defer { context.fulfill() } + context.reduceModel { model in model[keyPath: keyPath] = value } + } + } + + public struct FilterArrayAtKeyPath: ActionProtocol where V.Element == T { + public let keyPath: WritableKeyPath + public let isIncluded: (T) -> Bool + + public func reduce(context: TransactionContext, Self>) { + defer { context.fulfill() } + context.reduceModel { model in + guard var array = model[keyPath: keyPath] as? [T] else { + os_log(.error, log: OSLog.primary, " Arrays are the only collection type supported.") + return + } + array = array.filter(isIncluded) + // Trivial cast. + guard let collection = array as? V else { return } + model[keyPath: keyPath] = collection + } + } + } + +}