Skip to content

Commit a5c3753

Browse files
committed
Refactor controller view connection backward compatibility
1 parent f83d084 commit a5c3753

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

MobiusCore/Source/MobiusController.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,16 @@ public final class MobiusController<Model, Event, Effect> {
132132
/// The view should also return a `Connection` that accepts models and renders them. Disposing the connection should
133133
/// make the view stop emitting events.
134134
///
135+
/// - Parameter connectable: The view to connect.
135136
/// - Returns: An identifier that can be used to disconnect the view.
136137
/// - Attention: Fails via `MobiusHooks.errorHandler` if the loop is running.
137138
@discardableResult
138139
public func connectView<ViewConnectable: Connectable>(
139140
_ connectable: ViewConnectable
140141
) -> UUID where ViewConnectable.Input == Model, ViewConnectable.Output == Event {
141142
do {
142-
var uuid = Self.viewConnectableID
143+
let uuid = UUID()
143144
try state.mutate { stoppedState in
144-
if stoppedState.viewConnectables[uuid] != nil {
145-
// For backwards compatibility with id-less `disconnectView()` usage, we default
146-
// to the reserved connection id and generate a new one when needed.
147-
uuid = UUID()
148-
}
149145
stoppedState.viewConnectables[uuid] = AsyncDispatchQueueConnectable(connectable, acceptQueue: viewQueue)
150146
}
151147

@@ -168,10 +164,21 @@ public final class MobiusController<Model, Event, Effect> {
168164
/// disconnect
169165
public func disconnectView(id: UUID? = nil) {
170166
do {
171-
let uuid = id ?? Self.viewConnectableID
172167
try state.mutate { stoppedState in
168+
guard !stoppedState.viewConnectables.isEmpty else {
169+
throw ErrorMessage(message: "\(Self.debugTag): no view connected, cannot disconnect")
170+
}
171+
172+
// For backwards compatibility, only require an id when there is more than 1 connection
173+
guard
174+
id != nil || stoppedState.viewConnectables.count == 1,
175+
let uuid = id ?? stoppedState.viewConnectables.keys.first
176+
else {
177+
throw ErrorMessage(message: "\(Self.debugTag): missing view connection id, cannot disconnect")
178+
}
179+
173180
guard stoppedState.viewConnectables[uuid] != nil else {
174-
throw ErrorMessage(message: "\(Self.debugTag): invalid view connection id, cannot disconnect")
181+
throw ErrorMessage(message: "\(Self.debugTag): invalid view connection, cannot disconnect")
175182
}
176183

177184
stoppedState.viewConnectables[uuid] = nil
@@ -289,11 +296,6 @@ public final class MobiusController<Model, Event, Effect> {
289296
}
290297
}
291298

292-
/// Reserves a special identifier for the primary view connectable
293-
private static var viewConnectableID: UUID {
294-
return UUID(uuid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
295-
}
296-
297299
/// Simple error that just carries an error message out of a closure for us
298300
private struct ErrorMessage: Error {
299301
let message: String

MobiusCore/Test/MobiusControllerTests.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,28 @@ class MobiusControllerTests: QuickSpec {
224224
}
225225
it("should not allow disconnecting without a connection") {
226226
controller.connectView(view)
227-
228-
let secondaryView = RecordingTestConnectable(expectedQueue: self.viewQueue)
229-
let secondaryConnectionID = controller.connectView(secondaryView)
230-
231227
controller.disconnectView()
232-
controller.disconnectView(id: secondaryConnectionID)
233228

234229
expect(controller.disconnectView()).to(raiseError())
235-
expect(controller.disconnectView(id: secondaryConnectionID)).to(raiseError())
236-
expect(controller.disconnectView(id: UUID())).to(raiseError())
230+
}
231+
232+
describe("multiple view connections") {
233+
it("should not allow disconnecting without a connection id") {
234+
let secondaryView = RecordingTestConnectable(expectedQueue: self.viewQueue)
235+
236+
controller.connectView(view)
237+
controller.connectView(secondaryView)
238+
239+
expect(controller.disconnectView()).to(raiseError())
240+
}
241+
it("should not allow disconnecting an invalid connection id") {
242+
let secondaryView = RecordingTestConnectable(expectedQueue: self.viewQueue)
243+
244+
controller.connectView(view)
245+
controller.connectView(secondaryView)
246+
247+
expect(controller.disconnectView(id: UUID())).to(raiseError())
248+
}
237249
}
238250
}
239251
#endif

0 commit comments

Comments
 (0)