From 56cfec706c9cabccd70b236bfa24e3081d04d0e7 Mon Sep 17 00:00:00 2001 From: Takeshi Shimada Date: Fri, 31 Oct 2025 00:12:36 +0900 Subject: [PATCH 1/5] docs: Add @Observable requirement warning to Feature protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit warning to State associatedtype documentation that @Observable macro is required but cannot be enforced by type system. Missing this macro causes SwiftUI views to not update automatically. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Sources/Flow/Store/Feature.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Flow/Store/Feature.swift b/Sources/Flow/Store/Feature.swift index 12360f7..d4bd6b4 100644 --- a/Sources/Flow/Store/Feature.swift +++ b/Sources/Flow/Store/Feature.swift @@ -138,6 +138,10 @@ public protocol Feature: Sendable { /// ``` /// /// - Note: @Observable requires class types for SwiftUI observation + /// - Warning: Your State class **must** use the `@Observable` macro for SwiftUI integration. + /// The type system cannot enforce this requirement. Forgetting `@Observable` will cause + /// SwiftUI views to not update automatically when state changes, and the compiler will + /// not warn you. Always verify your State class has the `@Observable` annotation. associatedtype State: AnyObject /// The type representing the result returned from action processing. From 3370789985e73c9c4878b8df323222e873d7bd76 Mon Sep 17 00:00:00 2001 From: Takeshi Shimada Date: Fri, 31 Oct 2025 00:13:39 +0900 Subject: [PATCH 2/5] docs: Clarify state parameter in .run closure examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add inline comments and documentation note explaining that the state parameter in .run closures refers to the same instance as the outer state parameter (reference type semantics). This clarifies that mutations inside .run are immediately visible to outer scope. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Sources/Flow/Store/Feature.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/Flow/Store/Feature.swift b/Sources/Flow/Store/Feature.swift index d4bd6b4..2dfbe94 100644 --- a/Sources/Flow/Store/Feature.swift +++ b/Sources/Flow/Store/Feature.swift @@ -48,9 +48,9 @@ import Foundation /// switch action { /// case .login(let credentials): /// state.isLoading = true // ← Direct state mutation -/// return .run { state in // ← Async task +/// return .run { state in // ← Same state instance (reference type) /// let user = try await authService.login(credentials) -/// state.user = user +/// state.user = user // ← Mutations visible to outer scope /// state.isAuthenticated = true /// state.isLoading = false /// } @@ -69,6 +69,11 @@ import Foundation /// } /// ``` /// +/// - Note: In the `.run` closure, the `state` parameter refers to the same instance as the outer +/// `state` parameter (State is a reference type). All mutations inside `.run` are immediately +/// visible to the outer scope. This allows you to update state both before and during async +/// operations while maintaining a single source of truth. +/// /// ## Task Management /// Your action handlers can return different task types: /// From 24bee66d7bd505d18af671819b82b5f89c18f22c Mon Sep 17 00:00:00 2001 From: Takeshi Shimada Date: Fri, 31 Oct 2025 00:14:53 +0900 Subject: [PATCH 3/5] refactor: Re-export ActionExecution typealias in ActionHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ActionExecution typealias to ActionHandler.swift for better discoverability. Previously only defined in ActionProcessor.swift, users now have direct access to the type definition when working with ActionHandler. Includes comprehensive documentation and usage examples. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Flow/ActionHandler/ActionHandler.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Sources/Flow/ActionHandler/ActionHandler.swift b/Sources/Flow/ActionHandler/ActionHandler.swift index 5305e3b..882d663 100644 --- a/Sources/Flow/ActionHandler/ActionHandler.swift +++ b/Sources/Flow/ActionHandler/ActionHandler.swift @@ -1,5 +1,34 @@ import Foundation +/// Action execution closure that mutates state and returns a task. +/// +/// This typealias defines the signature for action processing logic used in ``ActionHandler``. +/// The closure receives an action and state, performs any necessary state mutations, +/// and returns an ``ActionTask`` for asynchronous side effects. +/// +/// All execution occurs on the **MainActor**, ensuring thread-safe state mutations. +/// +/// ## Example +/// ```swift +/// let execution: ActionExecution = { action, state in +/// switch action { +/// case .increment: +/// state.count += 1 +/// return .none +/// } +/// } +/// ``` +/// +/// ## Type Parameters +/// - `Action`: The action type to process (must be Sendable) +/// - `State`: The state type to mutate (must be AnyObject/reference type) +/// - `ActionResult`: The result type returned from action processing (must be Sendable) +/// +/// ## See Also +/// - ``ActionHandler/init(_:)`` +public typealias ActionExecution = + @MainActor (Action, State) async -> ActionTask + /// A facade for action processing with fluent method chaining capabilities that can return typed results. /// /// `ActionHandler` provides a clean, composable API for defining how your feature From 248a1598bd4142f99e3bfa50450c8410e09a6c65 Mon Sep 17 00:00:00 2001 From: Takeshi Shimada Date: Fri, 31 Oct 2025 00:15:32 +0900 Subject: [PATCH 4/5] docs: Document Feature.handle() call semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation clarifying that handle() is called once during Store initialization and the returned ActionHandler is reused for all subsequent actions. Prevents confusion about lifecycle management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Sources/Flow/Store/Feature.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Flow/Store/Feature.swift b/Sources/Flow/Store/Feature.swift index 2dfbe94..728c666 100644 --- a/Sources/Flow/Store/Feature.swift +++ b/Sources/Flow/Store/Feature.swift @@ -232,6 +232,11 @@ public protocol Feature: Sendable { /// ensuring thread-safe UI updates. It returns a ``ActionTask`` to handle /// any asynchronous side effects. /// + /// - Note: The `handle()` method is called **once** during Store initialization. + /// The returned ``ActionHandler`` instance is reused for all subsequent actions. + /// Do not call `handle()` multiple times or store it separately - let the Store + /// manage the ActionHandler lifecycle. + /// /// ## Example /// ```swift /// func handle() -> ActionHandler { From 92d7c61c7771d766bd96c65ce04683094d39f2d4 Mon Sep 17 00:00:00 2001 From: Takeshi Shimada Date: Fri, 31 Oct 2025 00:16:45 +0900 Subject: [PATCH 5/5] docs: Fill ActionHandler documentation gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive documentation for method chaining methods: - onError: Clarify that multiple calls replace previous handler - transform: Add detailed logging example showing use case - use: Document middleware execution order (registration order) Includes practical examples for each method to guide users. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Flow/ActionHandler/ActionHandler.swift | 65 ++++++++++++++++++- .../Flow/ActionHandler/ActionProcessor.swift | 7 -- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Sources/Flow/ActionHandler/ActionHandler.swift b/Sources/Flow/ActionHandler/ActionHandler.swift index 882d663..ce21128 100644 --- a/Sources/Flow/ActionHandler/ActionHandler.swift +++ b/Sources/Flow/ActionHandler/ActionHandler.swift @@ -26,7 +26,9 @@ import Foundation /// /// ## See Also /// - ``ActionHandler/init(_:)`` -public typealias ActionExecution = +/// +/// - Note: Type constraints match Feature protocol requirements to ensure consistency. +public typealias ActionExecution = @MainActor (Action, State) async -> ActionTask /// A facade for action processing with fluent method chaining capabilities that can return typed results. @@ -224,8 +226,25 @@ public final class ActionHandler Void) -> ActionHandler< Action, State, ActionResult > { @@ -234,8 +253,34 @@ extension ActionHandler { /// Transforms the task returned by action processing. /// + /// Use this to add cross-cutting concerns like logging, analytics, or monitoring + /// to all tasks without modifying individual action handlers. + /// /// - Parameter taskTransform: A closure that transforms the task /// - Returns: A new ActionHandler with task transformation + /// + /// ## Example: Add Logging to All Tasks + /// ```swift + /// ActionHandler { action, state in + /// // action processing + /// } + /// .transform { task in + /// switch task.operation { + /// case .run(let id, let name, let operation, let onError, let cancelInFlight, let priority): + /// return .run(id: id, name: name, priority: priority) { state in + /// print("Task '\(name ?? id)' starting") + /// let result = try await operation(state) + /// print("Task '\(name ?? id)' completed") + /// return result + /// } onError: { error, state in + /// print("Task '\(name ?? id)' failed: \(error)") + /// onError?(error, state) + /// } + /// default: + /// return task + /// } + /// } + /// ``` public func transform( _ taskTransform: @escaping (ActionTask) -> ActionTask @@ -245,8 +290,26 @@ extension ActionHandler { /// Adds custom middleware to the action processing pipeline. /// + /// Middleware is executed in the order it's added. Call `use` multiple times + /// to add multiple middlewares, and they will execute sequentially. + /// /// - Parameter middleware: The middleware to add /// - Returns: A new ActionHandler with the middleware added + /// + /// ## Example: Add Multiple Middlewares + /// ```swift + /// ActionHandler { action, state in + /// // action processing + /// } + /// .use(LoggingMiddleware()) // Executes first + /// .use(AnalyticsMiddleware()) // Executes second + /// .use(TimingMiddleware()) // Executes third + /// ``` + /// + /// - Note: Middleware executes in **registration order**: + /// - `beforeAction` hooks run in order (first → last) + /// - Action logic executes + /// - `afterAction` hooks run in order (first → last) public func use(_ middleware: some BaseActionMiddleware) -> ActionHandler< Action, State, ActionResult > { diff --git a/Sources/Flow/ActionHandler/ActionProcessor.swift b/Sources/Flow/ActionHandler/ActionProcessor.swift index 645c840..ff4f757 100644 --- a/Sources/Flow/ActionHandler/ActionProcessor.swift +++ b/Sources/Flow/ActionHandler/ActionProcessor.swift @@ -1,12 +1,5 @@ import Foundation -/// Action execution closure that mutates state and returns a task. -/// -/// - Note: Type constraints match Feature protocol requirements to ensure consistency. -public typealias ActionExecution = - @MainActor (Action, State) async -> - ActionTask - /// Error handler closure that can mutate state in response to errors. public typealias StateErrorHandler = (Error, State) -> Void