Skip to content

Conversation

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian commented Jul 30, 2025

Note: This PR is based on top of #27; please review that one first.

Some refactors and other changes that I've pulled out of the upcoming PRs for implementing ably/specification#350 and ably/specification#353. See commit messages for more details.

Summary by CodeRabbit

  • New Features

    • Added support for time-aware operations in live objects with a new clock abstraction.
    • Introduced async/await-compatible API for subscribing to live object updates.
    • Added detailed debug logging for proxy object lifecycle events.
  • Improvements

    • Enhanced equality checks for live map and primitive values.
    • Improved encapsulation and naming consistency for internal object properties.
    • Added thread-safe accessors for object IDs.
    • Simplified encoding and decoding of object operations by removing format-dependent initial value encoding.
    • Centralized and improved channel state validation with better error reporting.
  • Bug Fixes

    • Ensured consistent handling of timestamps and logging across live object operations.
  • Tests

    • Updated tests to use a mock clock and new APIs.
    • Added new test helpers for internal map entry creation.
    • Removed obsolete encoding tests.
  • Chores

    • Updated dependency to the latest version of ably-cocoa.

This is preparation for adding additional fields (e.g. tombstonedAt) per
RTLM3a in [1].

[1] ably/specification#350
We'll use this when setting the upcoming tombstonedAt value for objects
and map entries.

This was all generated by Cursor; my only change was to add some locking
into the mock class.
Per [1] at 488e932. Preparation for implementing this tombstoning spec.

[1] ably/specification#350
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
There isn't a spec PR for this yet, but it's needed in order to
implement the write spec [1]. Asked about it in [2]. It's implemented in
JS in [3].

Got Cursor to do this.

[1] ably/specification#353
[2] https://github.com/ably/specification/pull/353/files#r2228017382
[3] ably/ably-js#2065
This will be needed for the write API (to extract object IDs from the
entries that the user supplies when creating a LiveMap).
This will be needed for the write API (to incorporate the values that
the user supplies when creating a LiveObject).
This will be needed for the write API (to extract object IDs from the
entries that the user supplies when creating a LiveMap).
Generated by Cursor at my request. Useful for tests. Will refine this
(e.g. to hide the usage of AsyncStream, and maybe tweak the name) in #4.
@coderabbitai
Copy link

coderabbitai bot commented Jul 30, 2025

Walkthrough

This update introduces a new SimpleClock protocol and implements a default system clock. Clock dependencies are injected throughout internal and public LiveObjects classes, enabling time-aware operations and improved testability. Timestamp fields are added to protocol messages and map entries, and related APIs are updated. Test code is refactored to use a mock clock and new internal entry types.

Changes

Cohort / File(s) Change Summary
Clock Abstraction & Implementation
Sources/AblyLiveObjects/Internal/SimpleClock.swift, Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift
Introduced SimpleClock protocol and DefaultSimpleClock implementation for production; added MockSimpleClock for tests with thread-safe time control.
Timestamp Propagation in Protocols
Sources/AblyLiveObjects/Protocol/ObjectMessage.swift, Sources/AblyLiveObjects/Protocol/WireObjectMessage.swift
Added optional serialTimestamp to object and map entry messages; removed initialValueEncoding and simplified initialValue handling in operations; updated encoding/decoding logic.
Internal Data Structures and Entry Types
Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift, Sources/AblyLiveObjects/Internal/SyncObjectsPoolEntry.swift
Added InternalObjectsMapEntry struct (with tombstone and timestamp support) and SyncObjectsPoolEntry (object state + timestamp) for internal synchronization and storage.
LiveObject Core Refactors & Clock Injection
Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift, Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift, Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift, Sources/AblyLiveObjects/Internal/ObjectsPool.swift, Sources/AblyLiveObjects/Internal/DefaultInternalPlugin.swift, Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
Refactored to inject and propagate clock dependencies; updated methods to accept timestamps; introduced/used new entry types; improved encapsulation and naming; added InternalLiveObject protocol for polymorphism.
Equatable Conformance
Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift, Sources/AblyLiveObjects/Public/PublicTypes.swift
Added Equatable conformance to InternalLiveMapValue, LiveMapValue, and PrimitiveObjectValue; implemented custom equality logic.
Public API and Logging Enhancements
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift, Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift, Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift, Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift, Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift, Sources/AblyLiveObjects/Public/ARTRealtimeChannel+Objects.swift
Injected logger dependencies into public proxy objects and creation args; enhanced proxy lifecycle logging; updated initializers to accept loggers.
Test Refactors for Clock & Entry Types
Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift, Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift, Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift, Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift, Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
Updated tests to use MockSimpleClock, new internal entry types, and revised method signatures; removed/renamed test-only methods; adjusted test factories.
Test Cleanup & Removals
Tests/AblyLiveObjectsTests/ObjectMessageTests.swift, Tests/AblyLiveObjectsTests/WireObjectMessageTests.swift, Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
Removed tests for obsolete encoding logic; cleaned up references to removed fields or renamed accessors.
Dependency Update
ably-cocoa
Updated submodule/dependency reference to a newer commit.
CoreSDK & Channel State Validation
Sources/AblyLiveObjects/Internal/CoreSDK.swift, Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift
Renamed sendObject to publish with updated documentation; added centralized channel state validation method used in subscription logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PublicAPI
    participant Logger
    participant InternalObj
    participant Clock

    User->>PublicAPI: Create/Access LiveObject (Map/Counter)
    PublicAPI->>Logger: Initialize logger
    PublicAPI->>InternalObj: Create with logger, clock
    InternalObj->>Clock: Access now (timestamp)
    InternalObj->>InternalObj: Apply operation with timestamp
    InternalObj->>PublicAPI: Return updated state
Loading
sequenceDiagram
    participant ProtocolMsg
    participant InternalObj
    participant Clock

    ProtocolMsg->>InternalObj: Receive ObjectMessage (w/ serialTimestamp)
    InternalObj->>Clock: Use clock for time-based logic
    InternalObj->>InternalObj: Store/propagate serialTimestamp
    InternalObj->>InternalObj: Update state with timestamp awareness
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • maratal
  • umair-ably

Poem

A clock now ticks in every map and counter’s den,
With timestamps dancing through the code again.
Loggers watch with careful eyes,
As proxies spring and synchronize.
The tests all run with time to mock—
Hooray for change, says the rabbit with a hop! 🐇⏰

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch groundwork-for-deletions-and-writes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@lawrence-forooghian lawrence-forooghian changed the base branch from main to ECO-5337-subscriptions July 30, 2025 07:30
@lawrence-forooghian lawrence-forooghian force-pushed the groundwork-for-deletions-and-writes branch from e5e82bf to 5fc3ed3 Compare July 30, 2025 07:39
@github-actions github-actions bot temporarily deployed to staging/pull/42/AblyLiveObjects July 30, 2025 07:40 Inactive
@lawrence-forooghian lawrence-forooghian marked this pull request as ready for review July 30, 2025 07:48
@lawrence-forooghian
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jul 30, 2025

✅ Actions performed

Full review triggered.

@umair-ably
Copy link
Collaborator

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jul 31, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (1)

1-1: Clarify comment about tombstonedAt property.

The comment mentions a tombstonedAt property that doesn't exist in the current implementation. Consider updating the comment to accurately reflect the current structure or clarify that this refers to future changes.

-/// The entries stored in a `LiveMap`'s data. Same as an `ObjectsMapEntry` but with an additional `tombstonedAt` property, per RTLM3a. (This property will be added in an upcoming commit.)
+/// The entries stored in a `LiveMap`'s data. Same as an `ObjectsMapEntry` but with additional internal metadata for tombstone and timeserial tracking per RTLM3a.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 392fae3 and ec5e928.

📒 Files selected for processing (33)
  • Sources/AblyLiveObjects/Internal/CoreSDK.swift (3 hunks)
  • Sources/AblyLiveObjects/Internal/DefaultInternalPlugin.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (10 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (26 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (17 hunks)
  • Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift (2 hunks)
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift (8 hunks)
  • Sources/AblyLiveObjects/Internal/SimpleClock.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/SyncObjectsPoolEntry.swift (1 hunks)
  • Sources/AblyLiveObjects/Protocol/ObjectMessage.swift (8 hunks)
  • Sources/AblyLiveObjects/Protocol/WireObjectMessage.swift (10 hunks)
  • Sources/AblyLiveObjects/Public/ARTRealtimeChannel+Objects.swift (1 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift (1 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift (1 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (4 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift (4 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift (8 hunks)
  • Sources/AblyLiveObjects/Public/PublicTypes.swift (4 hunks)
  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift (2 hunks)
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift (3 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift (16 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (45 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/ObjectMessageTests.swift (0 hunks)
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift (18 hunks)
  • Tests/AblyLiveObjectsTests/WireObjectMessageTests.swift (0 hunks)
  • ably-cocoa (1 hunks)
💤 Files with no reviewable changes (2)
  • Tests/AblyLiveObjectsTests/WireObjectMessageTests.swift
  • Tests/AblyLiveObjectsTests/ObjectMessageTests.swift
🧰 Additional context used
🧠 Learnings (15)
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ably module inside the ably...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the Ably module inside the AblyLiveObjects library code (non-test code), use `import Ably`.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • Sources/AblyLiveObjects/Public/PublicTypes.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Sources/AblyLiveObjects/Internal/CoreSDK.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ablyplugin module inside th...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the AblyPlugin module inside the AblyLiveObjects library code (non-test code), use `internal import AblyPlugin`.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Sources/AblyLiveObjects/Public/ARTRealtimeChannel+Objects.swift
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Sources/AblyLiveObjects/Internal/DefaultInternalPlugin.swift
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • Sources/AblyLiveObjects/Public/PublicTypes.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Sources/AblyLiveObjects/Internal/CoreSDK.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Sources/AblyLiveObjects/Protocol/ObjectMessage.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to import the following modules in the tests, do so i...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When you need to import the following modules in the tests, do so in the following way: Ably: use `import Ably`; AblyLiveObjects: use `@testable import AblyLiveObjects`; AblyPlugin: use `import AblyPlugin`; do not do `internal import`.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : when creating `testsonly_` property declarations, do not write gene...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When creating `testsOnly_` property declarations, do not write generic comments of the form 'Test-only access to the private createOperationIsMerged property'; the meaning of these properties is already well understood.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : only add labels to test cases or suites when the label is different...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Only add labels to test cases or suites when the label is different to the name of the suite `struct` or test method.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
📚 Learning: applies to **/tests/**/*.swift : use the swift testing framework (`import testing`), not xctest....
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Use the Swift Testing framework (`import Testing`), not XCTest.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
📚 Learning: applies to **/tests/**/*.swift : do not use `fatalerror` in response to a test expectation failure. ...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Do not use `fatalError` in response to a test expectation failure. Favour the usage of Swift Testing's `#require` macro.

Applied to files:

  • Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: the liveobjects functionality is referred to in the specification simply as 'objects'....
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/specification.mdc:0-0
Timestamp: 2025-07-29T08:07:37.861Z
Learning: The LiveObjects functionality is referred to in the Specification simply as 'Objects'.

Applied to files:

  • Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/*.swift : when writing jsonvalue or wirevalue types, favour using the literal syntax e...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to **/*.swift : When writing JSONValue or WireValue types, favour using the literal syntax enabled by their conformance to the `ExpressibleBy*Literal` protocols where possible.

Applied to files:

  • Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift
  • Sources/AblyLiveObjects/Public/PublicTypes.swift
  • Sources/AblyLiveObjects/Protocol/WireObjectMessage.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to pass a logger to internal components in the tests,...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When you need to pass a logger to internal components in the tests, pass `TestLogger()`.

Applied to files:

  • Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : when writing tests, make sure to add comments that explain when som...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When writing tests, make sure to add comments that explain when some piece of test data is not important for the scenario being tested.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : add comments that explain when some piece of test data is not impor...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Add comments that explain when some piece of test data is not important for the scenario being tested.

Applied to files:

  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : follow the guidelines given under 'attributing tests to a spec poin...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Follow the guidelines given under 'Attributing tests to a spec point' in the file `CONTRIBUTING.md` in order to tag the unit tests with the relevant specification points. Make sure to follow the exact format of the comments as described in that file. Pay particular attention to the difference between the meaning of `@spec` and `@specPartial` and be sure not to write `@spec` multiple times for the same specification point.

Applied to files:

  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : when writing tests, follow the guidelines given under 'attributing ...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When writing tests, follow the guidelines given under 'Attributing tests to a spec point' in the file `CONTRIBUTING.md` in order to tag the unit tests with the relevant specification points. Make sure to follow the exact format of the comments as described in that file. Pay particular attention to the difference between the meaning of `@spec` and `@specPartial` and be sure not to write `@spec` multiple times for the same specification point.

Applied to files:

  • Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to unwrap an optional value in the tests, favour usin...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When you need to unwrap an optional value in the tests, favour using `#require` instead of `guard let`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
🧬 Code Graph Analysis (6)
Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift (1)
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift (1)
  • testsOnly_publish (78-80)
Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift (1)
Sources/AblyLiveObjects/Internal/CoreSDK.swift (1)
  • validateChannelState (56-68)
Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift (1)
Sources/AblyLiveObjects/Public/PublicTypes.swift (1)
  • lhs (139-156)
Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift (1)
Sources/AblyLiveObjects/Internal/CoreSDK.swift (1)
  • publish (32-39)
Sources/AblyLiveObjects/Public/PublicTypes.swift (4)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (1)
  • subscribe (100-114)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (1)
  • subscribe (179-193)
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift (1)
  • subscribe (37-39)
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1)
  • subscribe (86-88)
Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (3)
Sources/AblyLiveObjects/Internal/CoreSDK.swift (2)
  • validateChannelState (56-68)
  • publish (32-39)
Sources/AblyLiveObjects/Internal/ObjectsPool.swift (1)
  • createZeroValueObject (171-199)
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift (1)
  • testsOnly_publish (78-80)
🔇 Additional comments (95)
ably-cocoa (1)

1-1: Submodule bump looks correct – please ensure the workspace actually fetches the new commit.

Only the SHA has changed, so there is no functional code to review here.
Before merging, run git submodule update --init --recursive (or equivalent CI step) and open the Xcode workspace to confirm that the new ably-cocoa revision (c72e1d7…) builds cleanly and that there are no API mismatches against the plugin code.

Sources/AblyLiveObjects/Public/ARTRealtimeChannel+Objects.swift (1)

21-22: LGTM! Clean integration of logging support.

The addition of the logger parameter follows the established pattern and aligns with the broader logging enhancement across the codebase.

Also applies to: 27-27

Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift (1)

165-165: proxied is correctly exposed and matches the old test‐only accessor

I confirmed that in PublicDefaultLiveMap.swift there is

internal let proxied: InternalDefaultLiveMap

which returns the same underlying instance you previously accessed via testsOnly_proxied. Because tests use @testable import AblyLiveObjects, internal access is permitted, and assigning root.proxied to a weak variable exercises the same deallocation path as before.

Everything lines up—this API update is safe to approve.

Sources/AblyLiveObjects/Internal/DefaultInternalPlugin.swift (1)

41-41: DefaultSimpleClock Verified

The DefaultSimpleClock class is defined in Sources/AblyLiveObjects/Internal/SimpleClock.swift as an internal final class DefaultSimpleClock: SimpleClock. The dependency injection of DefaultSimpleClock() into InternalDefaultRealtimeObjects on line 41 of DefaultInternalPlugin.swift is correct and aligns with the intended architecture.

Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift (1)

91-91: LGTM! Good refactoring to centralize validation logic.

The change from manual channel state validation to using coreSDK.validateChannelState() improves maintainability by consolidating the validation logic into a reusable method while preserving the same error handling behavior.

Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift (1)

93-93: LGTM! Correct API rename in test code.

The changes from testsOnly_sendObject to testsOnly_publish correctly update the test calls to match the renamed method in the underlying implementation, maintaining the same functionality.

Also applies to: 113-113

Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift (1)

14-16: LGTM! Method rename aligns with protocol changes.

The method rename from sendObject to publish correctly maintains consistency with the updated CoreSDK protocol while preserving the mock's intended behavior of calling protocolRequirementNotImplemented().

Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (1)

1-8: Well-designed protocol for LiveObject polymorphism.

The InternalLiveObject protocol provides a clean abstraction for common LiveObject functionality with appropriate Sendable constraint on the Update type. The protocol design enables polymorphic access to mutable state across different LiveObject implementations, supporting the broader architectural refactoring goals.

Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (2)

2-6: Well-structured internal map entry with proper spec annotations.

The struct design appropriately extends ObjectsMapEntry with additional internal metadata. The property annotations (OME2a, OME2b, OME2c) correctly reference the specification.


8-14: Correct initialization from public ObjectsMapEntry.

The initializer properly copies all properties from the public ObjectsMapEntry type, maintaining consistency between internal and public representations.

Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift (3)

1-1: Correct internal import following established conventions.

The internal import AblyPlugin statement follows the learned convention for importing AblyPlugin within non-test library code.


9-9: Logger property addition supports enhanced observability.

Adding the logger property to PublicValueCreationArgs enables consistent logging propagation through the public proxy object creation flow.


12-12: Consistent logger propagation to creation arguments.

The logger parameter is correctly passed to both CounterCreationArgs and MapCreationArgs, maintaining consistency with the enhanced logging architecture.

Also applies to: 16-16

Sources/AblyLiveObjects/Internal/SyncObjectsPoolEntry.swift (2)

1-7: Well-designed struct for time-aware synchronization.

The SyncObjectsPoolEntry struct appropriately encapsulates both object state and timing metadata needed for the enhanced synchronization system. The documentation clearly explains its purpose in the OBJECT_SYNC sequence.


9-14: Excellent defensive programming with custom initializer.

The custom initializer that explicitly requires both parameters is a smart approach to prevent accidental omission of the objectMessageSerialTimestamp. This ensures proper timestamp tracking throughout the synchronization process.

Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift (1)

58-74: LGTM! Consistent Equatable implementation.

The equality logic correctly mirrors the public LiveMapValue implementation, using value equality for primitives and reference identity for live objects.

Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift (1)

13-13: LGTM! Proper mock clock injection for testing.

Adding MockSimpleClock() to the test helper correctly supports the new clock dependency, enabling deterministic time control in tests.

Sources/AblyLiveObjects/Internal/SimpleClock.swift (1)

7-19: LGTM! Clean clock abstraction design.

The SimpleClock protocol provides a focused interface for time operations with proper Sendable conformance for concurrent contexts. The DefaultSimpleClock implementation correctly uses the system clock. Clear documentation explains the testing benefits.

Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (3)

2-2: LGTM! Correct import style following coding guidelines.

Using internal import AblyPlugin correctly follows the established coding guidelines for non-test library code.


8-8: LGTM! Consistent logger dependency integration.

The changes properly integrate the logger dependency by making the proxied property internal, adding the logger property, and updating the initializer. This aligns with the broader logging enhancements across the codebase.

Also applies to: 14-14, 16-21


30-30: LGTM! Consistent logger propagation.

All creationArgs consistently include the logger parameter, ensuring proper logging integration throughout the proxy object creation process.

Also applies to: 50-50, 71-71

Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift (5)

13-13: LGTM! Consistent clock dependency injection in tests.

All counter creation calls properly include MockSimpleClock() to support the new clock dependency, enabling deterministic time control in tests.

Also applies to: 31-31, 47-47, 63-63, 90-90, 103-103, 119-119, 139-139, 161-161, 185-185, 201-201, 225-225, 266-266, 290-290, 328-328, 364-364, 404-404


35-35: LGTM! Updated method signatures with timestamp parameter.

All replaceData calls correctly include the new objectMessageSerialTimestamp: nil parameter, aligning with the updated method signatures that support timestamp-aware logic.

Also applies to: 51-51, 70-70, 80-80, 95-95, 107-107, 125-125, 143-143, 165-165, 206-206, 229-229, 270-270, 297-297, 371-371


132-132: LGTM! Method name updated to reflect non-test-only usage.

The method name change from testsOnly_mergeInitialValue to mergeInitialValue correctly reflects that this method is no longer test-only and is used in production code.

Also applies to: 148-148, 173-173, 189-189


303-303: LGTM! ObjectsPool creation with clock dependency.

All ObjectsPool creation calls properly include the clock dependency, maintaining consistency with the new constructor requirements.

Also applies to: 335-335, 378-378, 411-411


310-310: LGTM! Operation application with timestamp parameter.

All apply method calls correctly include the new objectMessageSerialTimestamp: nil parameter, supporting the enhanced timestamp-aware operation handling.

Also applies to: 342-342, 385-385, 416-416

Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift (1)

17-39: LGTM!

The mock clock implementation correctly uses mutex locking for thread-safe access to the current time, and provides a clear API for testing time-dependent functionality.

Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift (3)

12-15: LGTM!

The consistent addition of clock: MockSimpleClock() parameter across all test methods properly supports the new time-aware functionality. The tests maintain good structure with proper spec annotations.

Also applies to: 25-26, 40-43, 57-59, 67-69, 88-91, 126-129, 158-159, 185-186, 211-212, 233-237, 260-264, 280-282, 289-293


33-33: Good API improvement

The change from testsOnly_objectID to objectID makes the API cleaner by promoting this property from test-only to regular API usage.

Also applies to: 50-50, 175-175, 203-203, 357-357, 364-364


103-103: Consistent adaptation to new API

The wrapping of object states in SyncObjectsPoolEntry with objectMessageSerialTimestamp: nil is correctly applied throughout all test cases, properly adapting to the new time-aware synchronization API.

Also applies to: 138-138, 166-166, 194-194, 221-221, 246-246, 264-264, 327-327

Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift (1)

8-18: LGTM!

The addition of the logger dependency and simplification of the proxied property declaration improve the code structure. The dependency injection pattern is properly implemented.

Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift (2)

16-21: LGTM!

The logger dependency injection is consistently implemented, and it's properly passed through to PublicObjectsStore for proxy lifecycle logging.

Also applies to: 33-33


78-80: Method rename aligns with protocol changes

The rename from testsOnly_sendObject to testsOnly_publish correctly reflects the corresponding change in the CoreSDK protocol.

Sources/AblyLiveObjects/Public/PublicTypes.swift (3)

86-157: Well-designed Equatable implementation

The custom equality implementation correctly distinguishes between value equality for primitives and reference identity for live objects. This is the appropriate semantic for mutable live objects.


289-289: LGTM!

Adding Equatable conformance to PrimitiveObjectValue enables value comparison and is consistent with the LiveMapValue implementation.


399-420: Excellent async/await API addition

The updates() method provides a modern Swift concurrency interface that:

  • Properly wraps the callback-based subscription API
  • Handles cleanup via onTermination to prevent memory leaks
  • Enables idiomatic Swift code with for await loops
Sources/AblyLiveObjects/Internal/CoreSDK.swift (2)

8-9: Method rename looks good!

The rename from sendObject to publish aligns better with Ably's terminology and the documentation clearly indicates it implements the internal #publish method of RTO15.

Also applies to: 32-33


46-69: Well-structured channel state validation utility!

The new validateChannelState extension method provides a clean, reusable way to validate channel states with detailed error construction. This centralization will help maintain consistency across the codebase.

Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift (2)

344-344: Parameter simplification looks correct.

The change from Data? to String? for initialValue and removal of initialValueEncoding aligns with the protocol simplification mentioned in the summary.

Also applies to: 354-354


405-418: Internal factory methods are well-documented.

The new internalMapEntry and internalStringMapEntry factory methods are properly documented to indicate they should be kept in sync with their public counterparts. This will help prevent divergence between the internal and public APIs.

Also applies to: 437-454

Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift (2)

1-1: Import statement follows correct pattern.

Using internal import AblyPlugin is correct for non-test code in the AblyLiveObjects library.


71-88: Excellent structured logging implementation!

The addition of debug logging for proxy lifecycle events (creation, reuse, cleanup) provides valuable observability. The log messages include both proxy and proxied object identifiers, making debugging much easier.

Also applies to: 93-102

Sources/AblyLiveObjects/Protocol/WireObjectMessage.swift (2)

18-18: Timestamp support properly integrated.

The addition of serialTimestamp to wire messages and map entries is consistently implemented with proper encoding/decoding logic that follows the Ably protocol date format (milliseconds since epoch).

Also applies to: 32-32, 46-46, 102-102, 139-140, 439-439, 454-454, 469-470


161-274: Clean refactoring of WireObjectOperation.

The split into PartialWireObjectOperation and WireObjectOperation is well-designed, avoiding code duplication by delegating encoding/decoding logic. The change to String? for initialValue simplifies the protocol.

Sources/AblyLiveObjects/Internal/ObjectsPool.swift (3)

37-37: Timestamp propagation properly implemented.

The addition of objectMessageSerialTimestamp parameter through the apply and replaceData methods enables proper timestamp-aware handling of object updates. The documentation clearly explains the parameter's purpose.

Also applies to: 46-46, 54-54, 82-84


123-123: Clock dependency injection follows best practices.

The addition of SimpleClock parameter throughout initialization and object creation methods enables proper time-aware operations and improves testability by allowing mock clocks in tests.

Also applies to: 129-129, 137-137, 142-142, 169-171, 189-189, 191-191


203-203: SyncObjectsPoolEntry integration is well-structured.

The change from [ObjectState] to [SyncObjectsPoolEntry] properly encapsulates object state with its timestamp. The timestamp is correctly propagated to replaceData calls, enabling time-aware synchronization.

Also applies to: 206-206, 217-268

Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (9)

13-13: LGTM!

Correct addition of MockSimpleClock() as the clock parameter, consistent with the pattern used throughout the test file.


33-33: LGTM!

Consistent addition of the clock parameter.


41-44: LGTM!

Appropriate update to use the internal factory method internalMapEntry, consistent with the new internal data structure.


46-46: LGTM!

Consistent updates throughout these test methods:

  • Clock parameter added to all InternalDefaultLiveMap initializations
  • Factory methods updated to use internal variants
  • All changes follow the established pattern

Also applies to: 54-56, 66-68, 77-79, 88-90, 99-102


111-116: LGTM!

Clock parameter correctly added to both InternalDefaultLiveMap and InternalDefaultLiveCounter initializations, demonstrating consistent implementation across different live object types.

Also applies to: 130-132


156-162: LGTM!

Correct addition of:

  • Clock parameter to ObjectsPool initialization
  • objectMessageSerialTimestamp: nil to the replaceData method call

These changes align with the new timestamp-aware API.


171-180: LGTM!

All remaining changes follow the same consistent patterns:

  • MockSimpleClock() added to all object and pool initializations
  • objectMessageSerialTimestamp: nil added to all replaceData calls
  • Factory methods updated to use internal variants where applicable

The systematic nature of these changes demonstrates thorough refactoring across the entire test suite.

Also applies to: 187-187, 199-207, 220-237, 294-307, 342-351, 379-399, 440-447, 480-487, 552-554, 603-617, 652-657, 678-683, 718-718, 740-740, 803-810


830-830: Method rename handled correctly

The promotion of testsOnly_mergeInitialValue to mergeInitialValue has been consistently applied throughout the test file. This aligns with the method's promotion from test-only to main API status.

Also applies to: 848-848, 883-883, 922-922, 937-937, 956-956


1012-1021: LGTM!

The apply method calls have been correctly updated with the objectMessageSerialTimestamp: nil parameter, completing the systematic update of method signatures to support timestamp-aware operations.

Also applies to: 1028-1034, 1063-1069, 1115-1121, 1166-1172, 1197-1203

Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (8)

13-13: LGTM!

Addition of the clock property follows the established pattern of dependency injection for time-aware operations.


48-48: LGTM!

The type change to [SyncObjectsPoolEntry] correctly reflects the need to pair each object state with its serial timestamp during synchronization.


74-81: LGTM!

Clock dependency properly injected through the initializer and correctly propagated to MutableState and ObjectsPool initialization.


95-96: Good refactoring!

Using coreSDK.validateChannelState centralizes the validation logic and improves code maintainability. The method correctly validates against DETACHED and FAILED states as required by RTO1b.


167-176: LGTM!

Clock parameter correctly propagated through:

  • handleObjectProtocolMessage
  • handleObjectSyncProtocolMessage
  • testsOnly_createZeroValueLiveObject

This ensures consistent time source availability throughout the object handling pipeline.

Also applies to: 184-194, 200-203


206-211: Method rename is consistent

The rename from testsOnly_sendObject to testsOnly_publish correctly aligns with the CoreSDK API change. The method now calls coreSDK.publish instead of the previous sendObject method.


264-264: LGTM!

The implementation correctly:

  • Passes the clock parameter through the method
  • Constructs SyncObjectsPoolEntry objects pairing state with timestamp
  • Handles both multi-message sync sequences (RTO5a) and single-message syncs (RTO5a5)

The mapping logic properly filters out messages without object data while preserving the timestamp information.

Also applies to: 272-272, 301-307, 318-324, 334-334


345-345: LGTM!

Clock parameter consistently propagated through all remaining method calls:

  • applyObjectProtocolMessageObjectMessage
  • handleObjectProtocolMessage
  • apply operations

This completes the systematic addition of time-awareness throughout the internal realtime objects implementation.

Also applies to: 362-362, 382-382, 393-393, 410-410, 428-428

Sources/AblyLiveObjects/Protocol/ObjectMessage.swift (9)

17-17: LGTM!

Addition of serialTimestamp property to InboundObjectMessage per OM2j specification.


31-32: LGTM!

Addition of serialTimestamp property to OutboundObjectMessage maintains consistency with InboundObjectMessage.


34-45: Good architectural design

The introduction of PartialObjectOperation provides a clean separation for encoding operations without objectId. The change from Data? to String? for initialValue simplifies the encoding logic by removing format-dependent handling.


55-55: LGTM!

Type change to String? for initialValue is consistent with PartialObjectOperation.


82-82: LGTM!

Addition of serialTimestamp to ObjectsMapEntry per OME2d specification enables timestamp tracking at the entry level.


122-122: LGTM!

Wire format conversions correctly handle the new serialTimestamp field for both inbound and outbound messages.

Also applies to: 142-142


147-211: Well-structured delegation pattern

The implementation correctly:

  • Delegates encoding/decoding to PartialObjectOperation to avoid duplication
  • Preserves objectId separately as it's not part of the partial operation
  • Maintains clean separation of concerns between full and partial operations

This design makes the code more maintainable and reduces the chance of inconsistencies.


213-254: LGTM!

PartialObjectOperation implementation correctly:

  • Handles all operation fields except objectId
  • Sets nonce and initialValue to nil on inbound data per OOP3g/OOP3h
  • Simplifies initialValue encoding by passing it directly as String?

416-416: LGTM!

Wire conversion for ObjectsMapEntry correctly includes the new serialTimestamp field.

Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (11)

14-14: LGTM!

Property rename from liveObject to liveObjectMutableState improves clarity and is consistently applied throughout the accessor methods.

Also applies to: 20-20


26-26: LGTM!

Clock dependency properly injected through all constructors and initialization paths. The pattern is consistent with other internal live object implementations.

Also applies to: 35-36, 45-46, 50-50, 61-62, 68-68


72-78: Good addition

Thread-safe objectID accessor provides necessary access to the object identifier while maintaining proper synchronization.


84-84: LGTM!

Consistent use of coreSDK.validateChannelState for channel state validation, matching the pattern in other internal classes.


104-104: LGTM!

All references correctly updated to use liveObjectMutableState instead of the old liveObject property name.

Also applies to: 110-110, 118-118, 138-138


145-154: LGTM!

Method signature properly updated to accept objectMessageSerialTimestamp parameter with clear documentation explaining its purpose for tombstoning.


157-162: LGTM!

Method correctly promoted from testsOnly_mergeInitialValue to mergeInitialValue, consistent with the same change in InternalDefaultLiveMap.


178-196: LGTM!

The apply method signature correctly updated with objectMessageSerialTimestamp parameter and properly propagated to the mutable state.


201-201: Good architectural improvement

MutableState now conforms to InternalLiveObject protocol, providing a unified abstraction for internal live object state handling across different object types.


203-203: LGTM!

All references to the mutable state property have been consistently updated throughout the MutableState implementation.

Also applies to: 217-220, 249-249, 264-264, 271-271, 281-281, 286-286, 298-298


209-215: LGTM!

Method signatures in MutableState correctly updated with objectMessageSerialTimestamp parameter, maintaining consistency with the public interface.

Also applies to: 259-259

Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (11)

17-17: LGTM! Type change aligns with internal entry unification.

The change from ObjectsMapEntry to InternalObjectsMapEntry is consistent with the PR's goal of unifying internal data entry types.


31-31: LGTM! Property rename improves clarity.

The rename from liveObject to liveObjectMutableState better communicates the purpose of this property.

Also applies to: 37-37


43-43: LGTM! Clock injection improves testability.

Adding SimpleClock as a dependency enables deterministic testing and time-aware operations throughout the LiveMap implementation.

Also applies to: 53-53, 61-61, 71-71, 76-76, 89-89, 97-97


103-108: LGTM! Thread-safe objectID accessor.

The new objectID property provides proper thread-safe access to the object identifier, replacing the previous test-only accessor.


136-136: LGTM! Centralized tombstone checking improves maintainability.

The isEntryTombstoned helper method properly implements RTLM14 checks and centralizes the tombstone logic used in size() and entries().

Also applies to: 150-150, 663-666


228-228: LGTM! Timestamp parameter enables time-aware operations.

Adding objectMessageSerialTimestamp supports proper tombstoning and synchronization, which aligns with the PR's groundwork for deletions and writes.

Also applies to: 231-231, 236-236, 276-276, 284-284


366-366: LGTM! Proper conversion to internal entry type.

The conversion from ObjectsMapEntry to InternalObjectsMapEntry using mapValues with the initializer is clean and correct.


239-239: LGTM! Consistent clock propagation throughout.

The clock parameter is properly threaded through all methods that perform time-aware operations, ensuring consistent time handling across the codebase.

Also applies to: 253-253, 266-266, 288-288, 310-310, 375-375, 389-389, 411-411, 448-448, 467-467, 489-489, 519-519, 546-546, 628-628, 644-644


670-670: LGTM! Method signature updated for internal entry type.

The convertEntryToLiveMapValue method signature properly reflects the migration to InternalObjectsMapEntry.


114-114: Channel state validation confirmed

The validateChannelState method in Sources/AblyLiveObjects/Internal/CoreSDK.swift (lines 56–68) correctly:

  • Checks the channel’s current state against the provided invalidStates
  • Throws LiveObjectsError.objectsOperationFailedInvalidChannelState(...).toARTErrorInfo() when in an invalid state (ARTErrorInfo code 90001, statusCode 400)

This aligns with the RTLM5c, RTLM10c, and RTLM11c requirements. No further changes needed.


336-338: InternalLiveObject conformance verified

The MutableState struct in InternalDefaultLiveMap.swift correctly fulfills all requirements of the InternalLiveObject protocol:

  • It provides the liveObjectMutableState property of type LiveObjectMutableState<DefaultLiveMapUpdate>, which infers the associated Update type.
  • The protocol only mandates an associated Update: Sendable and that property, both of which are satisfied.

No changes needed.

Base automatically changed from ECO-5337-subscriptions to main July 31, 2025 13:17
Generated by Cursor at my request (although I largely replaced its
code). Useful for tests.
I'm trying to debug an issue and this will be helpful. Code largely
generated by Cursor at my instruction.
This is preparation for tombstoning an object per [1]; we'll need the
ObjectMessage's serialTimestamp.

[1] ably/specification#350
Preparation for making these methods perform tombstoning per [1] (not
implemented yet).

[1] ably/specification#350
The logic for this check is going to expand when we implement the
tombstoning spec [1], so let's DRY it up.

[1] ably/specification#350
The commit that we're currently using no longer exists due to rebases of
feature branches (which have now been merged).
We need a type that represents (when considering [1] at cb11ba8) RTO13's
"partial ObjectOperation"; namely an ObjectOperation without an objectId
property. This is something that is easy to represent in TypeScript but
a bit of a faff in Swift; we have to create a new type for it.

Code largely generated by Cursor at my instruction.

[1] ably/specification#353
Code largely generated by Cursor at my request.
Based on [1] at cb11ba8. Implementation deferred to #47.

[1] ably/specification#353
@lawrence-forooghian lawrence-forooghian merged commit edaf8b3 into main Aug 1, 2025
18 checks passed
@lawrence-forooghian lawrence-forooghian deleted the groundwork-for-deletions-and-writes branch August 1, 2025 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants