Skip to content

Commit

Permalink
Remove Initiate completely from MobiusLoop
Browse files Browse the repository at this point in the history
Initiation is a MobiusController concern.

Note behaviour change: MobiusLoop no longer triggers initiate logs.
  • Loading branch information
JensAyton committed Apr 6, 2020
1 parent fe55d72 commit f3c7802
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 49 deletions.
6 changes: 3 additions & 3 deletions MobiusCore/Source/EventProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ class EventProcessor<Model, Event, Effect>: Disposable, CustomDebugStringConvert
access = accessGuard
}

func start(from first: First<Model, Effect>) {
func start(from model: Model, effects: [Effect]) {
access.guard {
currentModel = first.model
currentModel = model

publisher.post(Next.next(first.model, effects: first.effects))
publisher.post(Next.next(model, effects: effects))

for event in queuedEvents {
accept(event)
Expand Down
30 changes: 2 additions & 28 deletions MobiusCore/Source/Mobius.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ public extension Mobius {
return Builder(
update: update,
effectHandler: effectHandler,
initiate: nil,
eventSource: AnyEventSource({ _ in AnonymousDisposable(disposer: {}) }),
eventConsumerTransformer: { $0 },
logger: AnyMobiusLogger(NoopLogger())
Expand Down Expand Up @@ -111,22 +110,19 @@ public extension Mobius {
struct Builder<Model, Event, Effect> {
private let update: Update<Model, Event, Effect>
private let effectHandler: AnyConnectable<Effect, Event>
private let initiate: Initiate<Model, Effect>?
private let eventSource: AnyEventSource<Event>
private let logger: AnyMobiusLogger<Model, Event, Effect>
private let eventConsumerTransformer: ConsumerTransformer<Event>

fileprivate init<EffectHandler: Connectable>(
update: Update<Model, Event, Effect>,
effectHandler: EffectHandler,
initiate: Initiate<Model, Effect>?,
eventSource: AnyEventSource<Event>,
eventConsumerTransformer: @escaping ConsumerTransformer<Event>,
logger: AnyMobiusLogger<Model, Event, Effect>
) where EffectHandler.Input == Effect, EffectHandler.Output == Event {
self.update = update
self.effectHandler = AnyConnectable(effectHandler)
self.initiate = initiate
self.eventSource = eventSource
self.logger = logger
self.eventConsumerTransformer = eventConsumerTransformer
Expand All @@ -150,7 +146,6 @@ public extension Mobius {
return Builder(
update: update,
effectHandler: effectHandler,
initiate: initiate,
eventSource: AnyEventSource(eventSource),
eventConsumerTransformer: eventConsumerTransformer,
logger: logger
Expand All @@ -169,7 +164,6 @@ public extension Mobius {
return Builder(
update: update,
effectHandler: effectHandler,
initiate: initiate,
eventSource: eventSource,
eventConsumerTransformer: eventConsumerTransformer,
logger: AnyMobiusLogger(logger)
Expand All @@ -196,7 +190,6 @@ public extension Mobius {
return Builder(
update: update,
effectHandler: effectHandler,
initiate: initiate,
eventSource: eventSource,
eventConsumerTransformer: { consumer in transformer(oldTransfomer(consumer)) },
logger: logger
Expand All @@ -209,19 +202,11 @@ public extension Mobius {
/// - initialModel: The model the loop should start with.
/// - effects: Zero or more effects to execute immediately.
public func start(from initialModel: Model, effects: [Effect] = []) -> MobiusLoop<Model, Event, Effect> {
// If no explicit initiator was given, create one that passes the model through unchanged and applies the
// given effects.
precondition(
self.initiate == nil || effects.isEmpty,
"A loop cannot use withInitiator and also specify initial effects in start"
)
let initiate = self.initiate ?? { First(model: $0, effects: effects) }

return MobiusLoop.createLoop(
update: update,
effectHandler: effectHandler,
initialModel: initialModel,
initiate: initiate,
effects: effects,
eventSource: eventSource,
eventConsumerTransformer: eventConsumerTransformer,
logger: logger
Expand Down Expand Up @@ -260,21 +245,10 @@ public extension Mobius {
builder: self,
initialModel: initialModel,
initiate: initiate,
logger: logger,
loopQueue: loopQueue,
viewQueue: viewQueue
)
}

/// Internal; called by `MobiusController`
func withInitiate(_ initiate: @escaping Initiate<Model, Effect>) -> Builder {
return Builder(
update: update,
effectHandler: effectHandler,
initiate: initiate,
eventSource: eventSource,
eventConsumerTransformer: eventConsumerTransformer,
logger: logger
)
}
}
}
16 changes: 13 additions & 3 deletions MobiusCore/Source/MobiusController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public final class MobiusController<Model, Event, Effect> {
builder: Mobius.Builder<Model, Event, Effect>,
initialModel: Model,
initiate: Initiate<Model, Effect>? = nil,
logger: AnyMobiusLogger<Model, Event, Effect>,
loopQueue loopTargetQueue: DispatchQueue,
viewQueue: DispatchQueue
) {
Expand Down Expand Up @@ -125,12 +126,21 @@ public final class MobiusController<Model, Event, Effect> {
}
}

var decoratedBuilder = builder.withEventConsumerTransformer(flipEventsToLoopQueue)
// Wrap initiator (if any) in a logger
let actualInitiate: Initiate<Model, Effect>
if let initiate = initiate {
decoratedBuilder = decoratedBuilder.withInitiate(initiate)
actualInitiate = LoggingInitiate(initiate, logger: logger).initiate
} else {
actualInitiate = { First(model: $0) }
}

loopFactory = { decoratedBuilder.start(from: $0) }
let decoratedBuilder = builder
.withEventConsumerTransformer(flipEventsToLoopQueue)

loopFactory = { model in
let first = actualInitiate(model)
return decoratedBuilder.start(from: first.model, effects: first.effects)
}
}

deinit {
Expand Down
10 changes: 7 additions & 3 deletions MobiusCore/Source/MobiusLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ public protocol MobiusLogger {
associatedtype Event
associatedtype Effect

/// Called right before the `Initiate` function is called.
/// Called right before the `Initiate` function is called.
///
/// This method mustn't block, as it'll hinder the loop from running. It will be called on the
/// same thread as the `Initiate` function.
/// This method is only called for `MobiusController`-managed loops.
///
/// This method mustn't block, as it'll hinder the loop from running. It will be called on the
/// same thread as the `Initiate` function.
///
/// - Parameter model: the model that will be passed to the initiate function
func willInitiate(model: Model)

/// Called right after the `Initiate` function is called.
///
/// This method is only called for `MobiusController`-managed loops.
///
/// This method mustn't block, as it'll hinder the loop from running. It will be called on the
/// same thread as the initiate function.
///
Expand Down
5 changes: 2 additions & 3 deletions MobiusCore/Source/MobiusLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,12 @@ public final class MobiusLoop<Model, Event, Effect>: Disposable, CustomDebugStri
update: Update<Model, Event, Effect>,
effectHandler: EffectHandler,
initialModel: Model,
initiate: @escaping Initiate<Model, Effect>,
effects: [Effect],
eventSource: AnyEventSource<Event>,
eventConsumerTransformer: ConsumerTransformer<Event>,
logger: AnyMobiusLogger<Model, Event, Effect>
) -> MobiusLoop where EffectHandler.Input == Effect, EffectHandler.Output == Event {
let accessGuard = ConcurrentAccessDetector()
let loggingInitiate = LoggingInitiate(initiate, logger: logger)
let loggingUpdate = update.logging(logger)
let workBag = WorkBag(accessGuard: accessGuard)

Expand Down Expand Up @@ -163,7 +162,7 @@ public final class MobiusLoop<Model, Event, Effect>: Disposable, CustomDebugStri
let nextConnection = nextPublisher.connect(to: nextConsumer)

// everything is hooked up, start processing!
eventProcessor.start(from: loggingInitiate.initiate(initialModel))
eventProcessor.start(from: initialModel, effects: effects)

return MobiusLoop(
eventProcessor: eventProcessor,
Expand Down
10 changes: 5 additions & 5 deletions MobiusCore/Test/EventProcessorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ class EventProcessorTests: QuickSpec {

describe("publishing") {
it("should post the first to the publisher as a next") {
eventProcessor.start(from: First(model: 1, effects: []))
eventProcessor.start(from: 1, effects: [])

expect(receivedModels).to(equal([1]))
}

it("should post nexts to the publisher") {
eventProcessor.start(from: First(model: 1, effects: []))
eventProcessor.start(from: 1, effects: [])

eventProcessor.accept(10)
eventProcessor.accept(200)
Expand All @@ -68,7 +68,7 @@ class EventProcessorTests: QuickSpec {

context("given a start from 1") {
beforeEach {
eventProcessor.start(from: First(model: 1, effects: []))
eventProcessor.start(from: 1, effects: [])
}

it("should track the current model from start") {
Expand All @@ -87,7 +87,7 @@ class EventProcessorTests: QuickSpec {
eventProcessor.accept(80)
eventProcessor.accept(400)

eventProcessor.start(from: First(model: 1, effects: []))
eventProcessor.start(from: 1, effects: [])

expect(receivedModels).to(equal([1, 81, 481]))
}
Expand All @@ -108,7 +108,7 @@ class EventProcessorTests: QuickSpec {

context("when the event processor has a First") {
it("should produce the appropriate debug description") {
eventProcessor.start(from: First(model: 1, effects: [2, 3]))
eventProcessor.start(from: 1, effects: [2, 3])
let description = String(reflecting: eventProcessor)
expect(description).to(contain("Optional(<1")) // Due to synced queue its hard to test a processor with events
}
Expand Down
4 changes: 0 additions & 4 deletions MobiusCore/Test/MobiusLoopTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,6 @@ class MobiusLoopTests: QuickSpec {
.start(from: "begin")
}

it("should log startup") {
expect(logger.logMessages).toEventually(equal(["willInitiate(begin)", "didInitiate(begin, First<String, String>(model: \"begin\", effects: []))"]))
}

it("should log updates") {
logger.clear()

Expand Down

0 comments on commit f3c7802

Please sign in to comment.