From 68fc5ebc10266ed20fe4b27753d15666d187d5bd Mon Sep 17 00:00:00 2001 From: Jens Ayton Date: Mon, 17 Feb 2020 11:13:20 +0100 Subject: [PATCH] Allow EffectRouter to be passed to Mobius.loop directly --- MobiusCore/Source/Mobius.swift | 83 ++++++++++++++++++++---- MobiusCore/Test/MobiusLoopTests.swift | 5 +- MobiusCore/Test/NonReentrancyTests.swift | 5 +- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/MobiusCore/Source/Mobius.swift b/MobiusCore/Source/Mobius.swift index 92bab342..39e6177a 100644 --- a/MobiusCore/Source/Mobius.swift +++ b/MobiusCore/Source/Mobius.swift @@ -59,10 +59,10 @@ public typealias Initiate = (Model) -> First public enum Mobius {} -// MARK: - Building a Mobius Loop - public extension Mobius { + // MARK: - Creating a Mobius Loop builder + /// Create a `Builder` to help you configure a `MobiusLoop` before starting it. /// /// The builder is immutable. When setting various properties, a new instance of a builder will be returned. @@ -71,16 +71,43 @@ public extension Mobius { /// Once done configuring the loop you can start the loop using `start(from:)`. /// /// - Parameters: - /// - update: the `Update` function of the loop - /// - effectHandler: an instance conforming to `Connectable`. Will be used to handle effects by the loop. + /// - update: the update function of the loop. + /// - effectHandler: the `EffectRouter` that will be used by the loop to handle effects. /// - Returns: a `Builder` instance that you can further configure before starting the loop + static func loop( + update: Update, + effectHandler: EffectRouter + ) -> Builder { + return Builder( + update: update, + effectHandler: effectHandler.asConnectable, + initiate: nil, + eventSource: AnyEventSource({ _ in AnonymousDisposable(disposer: {}) }), + eventConsumerTransformer: { $0 }, + logger: AnyMobiusLogger(NoopLogger()) + ) + } + + /// Create a `Builder` to help you configure a `MobiusLoop` before starting it. + /// + /// The builder is immutable. When setting various properties, a new instance of a builder will be returned. + /// It is therefore recommended to chain the loop configuration functions + /// + /// Once done configuring the loop you can start the loop using `start(from:)`. + /// + /// - Parameters: + /// - update: the update function of the loop. + /// - effectHandler: the entity that will be used by the loop to handle effects. An instance conforming to + /// `Connectable` with `Effect` as input and `Event` as output. **Note:** there is an overload which takes an + /// `EffectRouter` instead, which should be preferred in most cases. + /// - Returns: a `Builder` instance that you can further configure before starting the loop. static func loop( update: Update, effectHandler: EffectHandler ) -> Builder where EffectHandler.Input == Effect, EffectHandler.Output == Event { return Builder( update: update, - effectHandler: effectHandler, + effectHandler: AnyConnectable(effectHandler), initiate: nil, eventSource: AnyEventSource({ _ in AnonymousDisposable(disposer: {}) }), eventConsumerTransformer: { $0 }, @@ -88,11 +115,39 @@ public extension Mobius { ) } - /// A convenience version of `loop` that takes an unwrapped update function. + /// Create a `Builder` to help you configure a `MobiusLoop` before starting it. + /// + /// The builder is immutable. When setting various properties, a new instance of a builder will be returned. + /// It is therefore recommended to chain the loop configuration functions + /// + /// Once done configuring the loop you can start the loop using `start(from:)`. + /// + /// - Parameters: + /// - update: the update function of the loop. + /// - effectHandler: the `EffectRouter` that will be used by the loop to handle effects. + /// - Returns: a `Builder` instance that you can further configure before starting the loop + static func loop( + update: @escaping (Model, Event) -> Next, + effectHandler: EffectRouter + ) -> Builder { + return self.loop( + update: Update(update), + effectHandler: effectHandler + ) + } + + /// Create a `Builder` to help you configure a `MobiusLoop` before starting it. + /// + /// The builder is immutable. When setting various properties, a new instance of a builder will be returned. + /// It is therefore recommended to chain the loop configuration functions + /// + /// Once done configuring the loop you can start the loop using `start(from:)`. /// /// - Parameters: - /// - update: the update function of the loop - /// - effectHandler: an instance conforming to `Connectable`. Will be used to handle effects by the loop. + /// - update: the update function of the loop. + /// - effectHandler: the entity that will be used by the loop to handle effects. An instance conforming to + /// `Connectable` with `Effect` as input and `Event` as output. **Note:** there is an overload which takes an + /// `EffectRouter` instead, which should be preferred in most cases. /// - Returns: a `Builder` instance that you can further configure before starting the loop static func loop( update: @escaping (Model, Event) -> Next, @@ -117,22 +172,24 @@ public extension Mobius { private let logger: AnyMobiusLogger private let eventConsumerTransformer: ConsumerTransformer - fileprivate init( + fileprivate init( update: Update, - effectHandler: EffectHandler, + effectHandler: AnyConnectable, initiate: Initiate?, eventSource: AnyEventSource, eventConsumerTransformer: @escaping ConsumerTransformer, logger: AnyMobiusLogger - ) where EffectHandler.Input == Effect, EffectHandler.Output == Event { + ) { self.update = update - self.effectHandler = AnyConnectable(effectHandler) + self.effectHandler = effectHandler self.initiate = initiate self.eventSource = eventSource self.logger = logger self.eventConsumerTransformer = eventConsumerTransformer } + // MARK: - Configuring the builder + /// Return a copy of this builder with a new [event source]. /// /// If a `MobiusLoop` is created from the builder by calling `start`, the event source will be subscribed to @@ -204,6 +261,8 @@ public extension Mobius { ) } + // MARK: - Creating a loop + /// Create a `MobiusLoop` from the builder, and optionally dispatch one or more effects. /// /// - Parameters: diff --git a/MobiusCore/Test/MobiusLoopTests.swift b/MobiusCore/Test/MobiusLoopTests.swift index 1a9121ed..29df5014 100644 --- a/MobiusCore/Test/MobiusLoopTests.swift +++ b/MobiusCore/Test/MobiusLoopTests.swift @@ -288,12 +288,11 @@ class MobiusLoopTests: QuickSpec { } } let payload: (Int) -> Int? = { $0 } - let effectConnectable = EffectRouter() + let effectRouter = EffectRouter() .routeEffects(withPayload: payload).to(effectHandler) - .asConnectable let update = Update { (_: Int, _: Int) -> Next in Next.dispatchEffects([1]) } loop = Mobius - .loop(update: update, effectHandler: effectConnectable) + .loop(update: update, effectHandler: effectRouter) .start(from: 0) } afterEach { diff --git a/MobiusCore/Test/NonReentrancyTests.swift b/MobiusCore/Test/NonReentrancyTests.swift index 73dd3484..16c9e3e7 100644 --- a/MobiusCore/Test/NonReentrancyTests.swift +++ b/MobiusCore/Test/NonReentrancyTests.swift @@ -75,11 +75,10 @@ class NonReentrancyTests: QuickSpec { return AnonymousDisposable {} } - let effectConnectable = EffectRouter() + let effectRouter = EffectRouter() .routeEffects(equalTo: .testEffect).to(testEffectHandler) - .asConnectable - loop = Mobius.loop(update: update, effectHandler: effectConnectable) + loop = Mobius.loop(update: update, effectHandler: effectRouter) .start(from: 0) }