Skip to content

Commit

Permalink
Add pause and resume API's to Loop
Browse files Browse the repository at this point in the history
On some scenarios, it's useful to "pause" the `Loop` so that we stop
processing events for some reason (e.g. to stop a `Loop` backed
Service). Following the same reasoning, it becomes necessary to have a
"resume" mechanism so that the Loop starts processing events again.

Given `Loop` now starts automatically and `stop` is designed as a tear
down mechanism to be used on dealloc and dispose all observations, some
new API's are required so that we can unplug/replug feedbacks to
achieve the above mentioned pause/resume behavior.

## Changes

- Create new `unplugFeedbacks` and `replugFeedbacks` API's in
`Floodgate`, which disposes feedbacks observations and restores them,
respectively. Floodgate now retains the feedbacks passed in on
`bootstrap` to use them on `replugFeedbacks`.

- Add `pause` and `resume` API's to `LoopBoxBase`.

- Implement `pause` and `resume` API's in `RootLoopBox`, which unplug
and replug the feedbacks on the `Floodgate`, respectively.

- Implement `pause` and `resume` API's in `ScopedLoopBox`, which
forward the calls to their root, respectively.
  • Loading branch information
p4checo committed Jul 6, 2020
1 parent 49404fc commit e30f6e4
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Loop.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pod::Spec.new do |s|
s.watchos.deployment_target = '2.0'
s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/ReactiveCocoa/Loop.git", :tag => "#{s.version}" }
s.source_files = "Loop/*.{swift}"
s.source_files = "Loop/**/*.{swift}"

s.cocoapods_version = ">= 1.7.0"
s.swift_versions = ["5.0", "5.1"]
Expand Down
23 changes: 21 additions & 2 deletions Loop/Floodgate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ final class Floodgate<State, Event>: FeedbackEventConsumer<Event> {

private let queue = Atomic(QueueState())
private let reducer: (inout State, Event) -> Void
private let feedbackDisposables = CompositeDisposable()
private var feedbacks: [Loop<State, Event>.Feedback] = []
private var feedbackDisposables = CompositeDisposable()

init(state: State, reducer: @escaping (inout State, Event) -> Void) {
self.state = state
Expand All @@ -40,7 +41,25 @@ final class Floodgate<State, Event>: FeedbackEventConsumer<Event> {
dispose()
}

func bootstrap(with feedbacks: [FeedbackLoop<State, Event>.Feedback]) {
func bootstrap(with feedbacks: [Loop<State, Event>.Feedback]) {
self.feedbacks = feedbacks

for feedback in feedbacks {
// Pass `producer` which has replay-1 semantic.
feedbackDisposables += feedback.events(producer, self)
}

reducerLock.perform {
drainEvents()
}
}

func unplugFeedbacks() {
feedbackDisposables.dispose()
feedbackDisposables = CompositeDisposable()
}

func replugFeedbacks() {
for feedback in feedbacks {
// Pass `producer` which has replay-1 semantic.
feedbackDisposables += feedback.events(producer, self)
Expand Down
20 changes: 20 additions & 0 deletions Loop/LoopBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ internal class ScopedLoopBox<RootState, RootEvent, ScopedState, ScopedEvent>: Lo
event: { [eventTransform] in eventTransform(event($0)) }
)
}

override func pause() {
root.pause()
}

override func resume() {
root.resume()
}
}

internal class RootLoopBox<State, Event>: LoopBoxBase<State, Event> {
Expand Down Expand Up @@ -81,6 +89,14 @@ internal class RootLoopBox<State, Event>: LoopBoxBase<State, Event> {
ScopedLoopBox(root: self, value: scope, event: event)
}

override func pause() {
floodgate.unplugFeedbacks()
}

override func resume() {
floodgate.replugFeedbacks()
}

func start(with feedbacks: [Loop<State, Event>.Feedback]) {
floodgate.bootstrap(with: feedbacks + [input.feedback])
}
Expand Down Expand Up @@ -113,6 +129,10 @@ internal class LoopBoxBase<State, Event> {
) -> LoopBoxBase<S, E> {
subclassMustImplement()
}

func pause() { subclassMustImplement() }

func resume() { subclassMustImplement() }
}

@inline(never)
Expand Down
8 changes: 8 additions & 0 deletions Loop/Public/Loop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ public final class Loop<State, Event> {
box: box.scoped(to: scope, event: event)
)
}

public func pause() {
box.pause()
}

public func resume() {
box.resume()
}
}

extension Loop {
Expand Down

0 comments on commit e30f6e4

Please sign in to comment.