diff --git a/README.md b/README.md index a2e2cc0..f6a475a 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,12 @@ You add the ViewModel as a property wrapper to your view: @ViewModel var model: AnyReactor = MyViewModel().eraseToAnyReactor() ``` +`AnyReactor` does not start the wrapped reactor automatically. Start it explicitly: + +```swift +.task { model.start() } +``` + To access the current `State` you use: ```swift @@ -200,4 +206,3 @@ You can easily mock state for Xcode Previews by using `Stub` reactor implementat # License GoodReactor repository is released under the MIT license. See [LICENSE](LICENSE.md) for details. - diff --git a/Sources/GoodReactor/Core/Erased/AnyReactor.swift b/Sources/GoodReactor/Core/Erased/AnyReactor.swift index 5b7535c..7b13609 100644 --- a/Sources/GoodReactor/Core/Erased/AnyReactor.swift +++ b/Sources/GoodReactor/Core/Erased/AnyReactor.swift @@ -25,7 +25,8 @@ import SwiftUI /// /// Behavior: /// - State is accessed dynamically and mutated by reducing events on a concrete underlying reactor. -/// - Lifecycle: external subscriptions start automatically when `AnyReactor` is initialized (by `start()`-ing the base reactor). +/// - Lifecycle: `AnyReactor` does not automatically start the wrapped reactor. +/// Call `start()` explicitly to initialize external subscriptions. /// - Events from `send(action:)`, `send(action:) async`, and `send(destination:)` are forwarded to the base reactor. /// /// Example: @@ -58,9 +59,11 @@ import SwiftUI // MARK: - Initialization + /// Creates a type-erased wrapper around `base`. + /// + /// - note: This initializer does not call `start()` on `base`. public init(_ base: R) where R.Action == Action, R.Destination == Destination, R.State == State { self._box = AnyReactorBox(base) - base.start() } } @@ -151,4 +154,3 @@ public extension Reactor { } } - diff --git a/Sources/GoodReactor/Core/Reactor.swift b/Sources/GoodReactor/Core/Reactor.swift index 4727043..b794c76 100644 --- a/Sources/GoodReactor/Core/Reactor.swift +++ b/Sources/GoodReactor/Core/Reactor.swift @@ -155,6 +155,9 @@ import SwiftUI /// of the reactor. /// /// You can use multiple subscriptions to multiple external events. + /// + /// - important: Keep this function free of side effects, except calls to + /// ``subscribe(to:map:)``. This function may be invoked multiple times. func transform() #if canImport(Combine) @@ -329,6 +332,11 @@ public extension Reactor { /// uses Combine, subscribes the event stream and publishes the /// initial state. func start() { + let hasSubscriptions = MapTables.subscriptions.value(forKey: self)?.isEmpty == false + guard !hasSubscriptions else { + return + } + self.transform() #if canImport(Combine)