Skip to content

Commit 4724a05

Browse files
Add placeholder protocol for polymorphic LiveObject functionality
Preparation for adding the `tombstone` method from [1]. (This approach is a _bit_ weird but it's what I could think of that's compatible with the existing LiveObjectMutableState approach.) [1] ably/specification#350
1 parent 94efe5a commit 4724a05

File tree

3 files changed

+46
-38
lines changed

3 files changed

+46
-38
lines changed

Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ internal final class InternalDefaultLiveCounter: Sendable {
1111

1212
internal var testsOnly_siteTimeserials: [String: String] {
1313
mutex.withLock {
14-
mutableState.liveObject.siteTimeserials
14+
mutableState.liveObjectMutableState.siteTimeserials
1515
}
1616
}
1717

1818
internal var testsOnly_createOperationIsMerged: Bool {
1919
mutex.withLock {
20-
mutableState.liveObject.createOperationIsMerged
20+
mutableState.liveObjectMutableState.createOperationIsMerged
2121
}
2222
}
2323

2424
internal var testsOnly_objectID: String {
2525
mutex.withLock {
26-
mutableState.liveObject.objectID
26+
mutableState.liveObjectMutableState.objectID
2727
}
2828
}
2929

@@ -50,7 +50,7 @@ internal final class InternalDefaultLiveCounter: Sendable {
5050
userCallbackQueue: DispatchQueue,
5151
clock: SimpleClock
5252
) {
53-
mutableState = .init(liveObject: .init(objectID: objectID), data: data)
53+
mutableState = .init(liveObjectMutableState: .init(objectID: objectID), data: data)
5454
self.logger = logger
5555
self.userCallbackQueue = userCallbackQueue
5656
self.clock = clock
@@ -106,21 +106,21 @@ internal final class InternalDefaultLiveCounter: Sendable {
106106
internal func subscribe(listener: @escaping LiveObjectUpdateCallback<DefaultLiveCounterUpdate>, coreSDK: CoreSDK) throws(ARTErrorInfo) -> any SubscribeResponse {
107107
try mutex.ablyLiveObjects_withLockWithTypedThrow { () throws(ARTErrorInfo) in
108108
// swiftlint:disable:next trailing_closure
109-
try mutableState.liveObject.subscribe(listener: listener, coreSDK: coreSDK, updateSelfLater: { [weak self] action in
109+
try mutableState.liveObjectMutableState.subscribe(listener: listener, coreSDK: coreSDK, updateSelfLater: { [weak self] action in
110110
guard let self else {
111111
return
112112
}
113113

114114
mutex.withLock {
115-
action(&mutableState.liveObject)
115+
action(&mutableState.liveObjectMutableState)
116116
}
117117
})
118118
}
119119
}
120120

121121
internal func unsubscribeAll() {
122122
mutex.withLock {
123-
mutableState.liveObject.unsubscribeAll()
123+
mutableState.liveObjectMutableState.unsubscribeAll()
124124
}
125125
}
126126

@@ -140,7 +140,7 @@ internal final class InternalDefaultLiveCounter: Sendable {
140140
/// This is used to instruct this counter to emit updates during an `OBJECT_SYNC`.
141141
internal func emit(_ update: LiveObjectUpdate<DefaultLiveCounterUpdate>) {
142142
mutex.withLock {
143-
mutableState.liveObject.emit(update, on: userCallbackQueue)
143+
mutableState.liveObjectMutableState.emit(update, on: userCallbackQueue)
144144
}
145145
}
146146

@@ -195,20 +195,20 @@ internal final class InternalDefaultLiveCounter: Sendable {
195195

196196
// MARK: - Mutable state and the operations that affect it
197197

198-
private struct MutableState {
198+
private struct MutableState: InternalLiveObject {
199199
/// The mutable state common to all LiveObjects.
200-
internal var liveObject: LiveObjectMutableState<DefaultLiveCounterUpdate>
200+
internal var liveObjectMutableState: LiveObjectMutableState<DefaultLiveCounterUpdate>
201201

202202
/// The internal data that this map holds, per RTLC3.
203203
internal var data: Double
204204

205205
/// Replaces the internal data of this counter with the provided ObjectState, per RTLC6.
206206
internal mutating func replaceData(using state: ObjectState) -> LiveObjectUpdate<DefaultLiveCounterUpdate> {
207207
// RTLC6a: Replace the private siteTimeserials with the value from ObjectState.siteTimeserials
208-
liveObject.siteTimeserials = state.siteTimeserials
208+
liveObjectMutableState.siteTimeserials = state.siteTimeserials
209209

210210
// RTLC6b: Set the private flag createOperationIsMerged to false
211-
liveObject.createOperationIsMerged = false
211+
liveObjectMutableState.createOperationIsMerged = false
212212

213213
// RTLC6c: Set data to the value of ObjectState.counter.count, or to 0 if it does not exist
214214
data = state.counter?.count?.doubleValue ?? 0
@@ -237,7 +237,7 @@ internal final class InternalDefaultLiveCounter: Sendable {
237237
}
238238

239239
// RTLC10b: Set the private flag createOperationIsMerged to true
240-
liveObject.createOperationIsMerged = true
240+
liveObjectMutableState.createOperationIsMerged = true
241241

242242
return update
243243
}
@@ -251,14 +251,14 @@ internal final class InternalDefaultLiveCounter: Sendable {
251251
logger: Logger,
252252
userCallbackQueue: DispatchQueue,
253253
) {
254-
guard let applicableOperation = liveObject.canApplyOperation(objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
254+
guard let applicableOperation = liveObjectMutableState.canApplyOperation(objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
255255
// RTLC7b
256256
logger.log("Operation \(operation) (serial: \(String(describing: objectMessageSerial)), siteCode: \(String(describing: objectMessageSiteCode))) should not be applied; discarding", level: .debug)
257257
return
258258
}
259259

260260
// RTLC7c
261-
liveObject.siteTimeserials[applicableOperation.objectMessageSiteCode] = applicableOperation.objectMessageSerial
261+
liveObjectMutableState.siteTimeserials[applicableOperation.objectMessageSiteCode] = applicableOperation.objectMessageSerial
262262

263263
switch operation.action {
264264
case .known(.counterCreate):
@@ -268,12 +268,12 @@ internal final class InternalDefaultLiveCounter: Sendable {
268268
logger: logger,
269269
)
270270
// RTLC7d1a
271-
liveObject.emit(update, on: userCallbackQueue)
271+
liveObjectMutableState.emit(update, on: userCallbackQueue)
272272
case .known(.counterInc):
273273
// RTLC7d2
274274
let update = applyCounterIncOperation(operation.counterOp)
275275
// RTLC7d2a
276-
liveObject.emit(update, on: userCallbackQueue)
276+
liveObjectMutableState.emit(update, on: userCallbackQueue)
277277
default:
278278
// RTLC7d3
279279
logger.log("Operation \(operation) has unsupported action for LiveCounter; discarding", level: .warn)
@@ -285,7 +285,7 @@ internal final class InternalDefaultLiveCounter: Sendable {
285285
_ operation: ObjectOperation,
286286
logger: Logger,
287287
) -> LiveObjectUpdate<DefaultLiveCounterUpdate> {
288-
if liveObject.createOperationIsMerged {
288+
if liveObjectMutableState.createOperationIsMerged {
289289
// RTLC8b
290290
logger.log("Not applying COUNTER_CREATE because a COUNTER_CREATE has already been applied", level: .warn)
291291
return .noop

Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal final class InternalDefaultLiveMap: Sendable {
2222

2323
internal var testsOnly_objectID: String {
2424
mutex.withLock {
25-
mutableState.liveObject.objectID
25+
mutableState.liveObjectMutableState.objectID
2626
}
2727
}
2828

@@ -34,13 +34,13 @@ internal final class InternalDefaultLiveMap: Sendable {
3434

3535
internal var testsOnly_siteTimeserials: [String: String] {
3636
mutex.withLock {
37-
mutableState.liveObject.siteTimeserials
37+
mutableState.liveObjectMutableState.siteTimeserials
3838
}
3939
}
4040

4141
internal var testsOnly_createOperationIsMerged: Bool {
4242
mutex.withLock {
43-
mutableState.liveObject.createOperationIsMerged
43+
mutableState.liveObjectMutableState.createOperationIsMerged
4444
}
4545
}
4646

@@ -76,7 +76,7 @@ internal final class InternalDefaultLiveMap: Sendable {
7676
userCallbackQueue: DispatchQueue,
7777
clock: SimpleClock,
7878
) {
79-
mutableState = .init(liveObject: .init(objectID: objectID), data: data, semantics: semantics)
79+
mutableState = .init(liveObjectMutableState: .init(objectID: objectID), data: data, semantics: semantics)
8080
self.logger = logger
8181
self.userCallbackQueue = userCallbackQueue
8282
self.clock = clock
@@ -201,21 +201,21 @@ internal final class InternalDefaultLiveMap: Sendable {
201201
internal func subscribe(listener: @escaping LiveObjectUpdateCallback<DefaultLiveMapUpdate>, coreSDK: CoreSDK) throws(ARTErrorInfo) -> any SubscribeResponse {
202202
try mutex.ablyLiveObjects_withLockWithTypedThrow { () throws(ARTErrorInfo) in
203203
// swiftlint:disable:next trailing_closure
204-
try mutableState.liveObject.subscribe(listener: listener, coreSDK: coreSDK, updateSelfLater: { [weak self] action in
204+
try mutableState.liveObjectMutableState.subscribe(listener: listener, coreSDK: coreSDK, updateSelfLater: { [weak self] action in
205205
guard let self else {
206206
return
207207
}
208208

209209
mutex.withLock {
210-
action(&mutableState.liveObject)
210+
action(&mutableState.liveObjectMutableState)
211211
}
212212
})
213213
}
214214
}
215215

216216
internal func unsubscribeAll() {
217217
mutex.withLock {
218-
mutableState.liveObject.unsubscribeAll()
218+
mutableState.liveObjectMutableState.unsubscribeAll()
219219
}
220220
}
221221

@@ -235,7 +235,7 @@ internal final class InternalDefaultLiveMap: Sendable {
235235
/// This is used to instruct this map to emit updates during an `OBJECT_SYNC`.
236236
internal func emit(_ update: LiveObjectUpdate<DefaultLiveMapUpdate>) {
237237
mutex.withLock {
238-
mutableState.liveObject.emit(update, on: userCallbackQueue)
238+
mutableState.liveObjectMutableState.emit(update, on: userCallbackQueue)
239239
}
240240
}
241241

@@ -346,9 +346,9 @@ internal final class InternalDefaultLiveMap: Sendable {
346346

347347
// MARK: - Mutable state and the operations that affect it
348348

349-
private struct MutableState {
349+
private struct MutableState: InternalLiveObject {
350350
/// The mutable state common to all LiveObjects.
351-
internal var liveObject: LiveObjectMutableState<DefaultLiveMapUpdate>
351+
internal var liveObjectMutableState: LiveObjectMutableState<DefaultLiveMapUpdate>
352352

353353
/// The internal data that this map holds, per RTLM3.
354354
internal var data: [String: InternalObjectsMapEntry]
@@ -368,10 +368,10 @@ internal final class InternalDefaultLiveMap: Sendable {
368368
userCallbackQueue: DispatchQueue,
369369
) -> LiveObjectUpdate<DefaultLiveMapUpdate> {
370370
// RTLM6a: Replace the private siteTimeserials with the value from ObjectState.siteTimeserials
371-
liveObject.siteTimeserials = state.siteTimeserials
371+
liveObjectMutableState.siteTimeserials = state.siteTimeserials
372372

373373
// RTLM6b: Set the private flag createOperationIsMerged to false
374-
liveObject.createOperationIsMerged = false
374+
liveObjectMutableState.createOperationIsMerged = false
375375

376376
// RTLM6c: Set data to ObjectState.map.entries, or to an empty map if it does not exist
377377
data = state.map?.entries?.mapValues { .init(objectsMapEntry: $0) } ?? [:]
@@ -428,7 +428,7 @@ internal final class InternalDefaultLiveMap: Sendable {
428428
}
429429

430430
// RTLM17b: Set the private flag createOperationIsMerged to true
431-
liveObject.createOperationIsMerged = true
431+
liveObjectMutableState.createOperationIsMerged = true
432432

433433
// RTLM17c: Merge the updates, skipping no-ops
434434
// I don't love having to use uniqueKeysWithValues, when I shouldn't have to. I should be able to reason _statically_ that there are no overlapping keys. The problem that we're trying to use LiveMapUpdate throughout instead of something more communicative. But I don't know what's to come in the spec so I don't want to mess with this internal interface.
@@ -457,14 +457,14 @@ internal final class InternalDefaultLiveMap: Sendable {
457457
userCallbackQueue: DispatchQueue,
458458
clock: SimpleClock,
459459
) {
460-
guard let applicableOperation = liveObject.canApplyOperation(objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
460+
guard let applicableOperation = liveObjectMutableState.canApplyOperation(objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
461461
// RTLM15b
462462
logger.log("Operation \(operation) (serial: \(String(describing: objectMessageSerial)), siteCode: \(String(describing: objectMessageSiteCode))) should not be applied; discarding", level: .debug)
463463
return
464464
}
465465

466466
// RTLM15c
467-
liveObject.siteTimeserials[applicableOperation.objectMessageSiteCode] = applicableOperation.objectMessageSerial
467+
liveObjectMutableState.siteTimeserials[applicableOperation.objectMessageSiteCode] = applicableOperation.objectMessageSerial
468468

469469
switch operation.action {
470470
case .known(.mapCreate):
@@ -477,7 +477,7 @@ internal final class InternalDefaultLiveMap: Sendable {
477477
clock: clock,
478478
)
479479
// RTLM15d1a
480-
liveObject.emit(update, on: userCallbackQueue)
480+
liveObjectMutableState.emit(update, on: userCallbackQueue)
481481
case .known(.mapSet):
482482
guard let mapOp = operation.mapOp else {
483483
logger.log("Could not apply MAP_SET since operation.mapOp is missing", level: .warn)
@@ -499,7 +499,7 @@ internal final class InternalDefaultLiveMap: Sendable {
499499
clock: clock,
500500
)
501501
// RTLM15d2a
502-
liveObject.emit(update, on: userCallbackQueue)
502+
liveObjectMutableState.emit(update, on: userCallbackQueue)
503503
case .known(.mapRemove):
504504
guard let mapOp = operation.mapOp else {
505505
return
@@ -511,7 +511,7 @@ internal final class InternalDefaultLiveMap: Sendable {
511511
operationTimeserial: applicableOperation.objectMessageSerial,
512512
)
513513
// RTLM15d3a
514-
liveObject.emit(update, on: userCallbackQueue)
514+
liveObjectMutableState.emit(update, on: userCallbackQueue)
515515
default:
516516
// RTLM15d4
517517
logger.log("Operation \(operation) has unsupported action for LiveMap; discarding", level: .warn)
@@ -637,7 +637,7 @@ internal final class InternalDefaultLiveMap: Sendable {
637637
userCallbackQueue: DispatchQueue,
638638
clock: SimpleClock,
639639
) -> LiveObjectUpdate<DefaultLiveMapUpdate> {
640-
if liveObject.createOperationIsMerged {
640+
if liveObjectMutableState.createOperationIsMerged {
641641
// RTLM16b
642642
logger.log("Not applying MAP_CREATE because a MAP_CREATE has already been applied", level: .warn)
643643
return .noop
@@ -663,7 +663,7 @@ internal final class InternalDefaultLiveMap: Sendable {
663663

664664
// RTO4b2a
665665
let mapUpdate = DefaultLiveMapUpdate(update: previousData.mapValues { _ in .removed })
666-
liveObject.emit(.update(mapUpdate), on: userCallbackQueue)
666+
liveObjectMutableState.emit(.update(mapUpdate), on: userCallbackQueue)
667667
}
668668
}
669669

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/// Provides RTLO spec point functionality common to all LiveObjects.
2+
///
3+
/// This exists in addition to ``LiveObjectMutableState`` to enable polymorphism.
4+
internal protocol InternalLiveObject<Update> {
5+
associatedtype Update: Sendable
6+
7+
var liveObjectMutableState: LiveObjectMutableState<Update> { get set }
8+
}

0 commit comments

Comments
 (0)