[WIP] Prototype pulling LiveObjects plugin into this repo#2205
Draft
lawrence-forooghian wants to merge 304 commits into
Draft
[WIP] Prototype pulling LiveObjects plugin into this repo#2205lawrence-forooghian wants to merge 304 commits into
lawrence-forooghian wants to merge 304 commits into
Conversation
We adopt a pattern whereby the LiveObjects SDK functions correctly as long as the user is holding a reference to any object vended by the public API of the SDK. We do this by copying the ably-cocoa approach of having separate public and internal versions of objects. All written by me, except for getting Cursor to help with updating the tests in response to the new way of injecting CoreSDK. Resolves #9.
It's a bit annoying that Xcode's autocomplete didn't do this when I created these implementations.
Missed this in ce8c022.
Didn't do this in ce8c022 because I didn't have a good idea of our threading approach. But for the initial approach that we'll be taking — namely, calling the callbacks on ably-cocoa's callback queue — I think we'll need it. Haven't done it for batch stuff yet because I don't yet know whether it'll be necessary (will get a better idea when we implement it). Now that the callback can be called on any thread, we can no longer easily use the "capture the return value of `subscribe` pattern so that we can unsubscribe later" pattern. So, I've decided to pass the subscripiton as a second argument to the callback. Might be there's a better pattern we can use (e.g. pass in an object to the `subscribe` call, like JS `fetch`'s AbortController), but this'll do for now.
The correct behaviour wasn't clear from the spec when I wrote cb427d8, but new spec PR [1] makes it seem that this is the right thing to do (still needs clarifying though). [1] ably/specification#346
Mistake in 6430358.
This is preparation for implementing subscriptions. Cursor updated the tests.
Motivation as in 3f6de86; the new spec points in [1] tell us these can throw. [1] ably/specification#346
[ECO-5328] Further initial setup of plugin
[ECO-5329] Implement the protocol-level interactions with ably-cocoa
[ECO-5390] Encode and decode binary and JSON data per spec
[ECO-5416, ECO-5417] Rename some things to match spec
Groundwork for object sync
[ECO-5332] Implement `OBJECT_SYNC`
[ECO-5330] Implement remaining `LiveMap` access API
[ECO-5333] Apply `OBJECT` `ProtocolMessage` operations
[ECO-5334] Buffer `OBJECT` ProtocolMessage operations during a sync
Use an abstract channel
[ECO-5384] Implement memory management pattern similar to ably-cocoa
Based on [1] at 2963300. Have not implemented RTL04b1's channel mode checking for same reason as mentioned in 8d881e2. Have not currently tested `replaceData`'s return value; will do once [2] clarified. [1] ably/specification#346 [2] https://github.com/ably/specification/pull/346/files#r2201363446
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.
Bump plugin-support to 1.1.0
Merge `integration/protocol-v6` into `main`
Since we're doing a v2 release of plugin-support, the @optional workarounds introduced in 8bfbbaa are no longer needed. Make all methods required, convert the siteCode getter method back to a property, and fold the completionWithResult: variant back into the original completion: method. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge `integration/protocol-v6` into `main`
Remove backwards-compatibility scaffolding from apply-on-ACK APIs
Since plugin-support v2 makes all previously @optional methods required, the backwards-compatibility scaffolding introduced alongside them is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bump plugin-support to v2
Release version 0.4.0
Prepares this repo to be merged into ably-cocoa under a sub-directory of that name. Done as a single move commit so that the subsequent merge into ably-cocoa is conflict-free at every path (no top-level collisions with ably-cocoa's .gitignore, COPYRIGHT, Package.swift, README.md, or LICENSE), while every pre-move commit on this branch retains its original SHA. Cross-references to those SHAs from elsewhere (PR descriptions, commit messages) continue to resolve. Original file history is reachable via `git log --follow` on the new paths, or by walking from the plugin-support-pre-merge tag in ably-cocoa. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prepares this repo to be merged into ably-cocoa under a sub-directory of that name. Done as a single move commit so that the subsequent merge into ably-cocoa is conflict-free at every path, while every pre-move commit on this branch retains its original SHA. Cross-references to those SHAs from elsewhere (PR descriptions, commit messages) continue to resolve. Note: the ably-common submodule's registration moves with the rest of the repo to merged-repos/.../.gitmodules. Git only consults a .gitmodules file at the repo root, so submodule operations against this branch will fail until the submodule is re-registered at the new path in ably-cocoa's root .gitmodules — to be done as a follow-up commit after the merge. Original file history is reachable via `git log --follow` on the new paths, or by walking from the liveobjects-plugin-pre-merge tag in ably-cocoa. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings every commit from the ably-cocoa-plugin-support repository into ably-cocoa's history, with the entire repo contents living under merged-repos/ably-cocoa-plugin-support/. The relocation happened in the move commit fd04eab on the plugin-support side, so this merge introduces zero path collisions and zero conflicts. Every pre-move commit retains its original SHA. The pre-merge tip is preserved as the tag 'plugin-support-pre-merge' for queries that want to walk the original repo's main branch from the original tip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings every commit from the ably-liveobjects-swift-plugin repository into ably-cocoa's history, with the entire repo contents living under merged-repos/ably-liveobjects-swift-plugin/. The relocation happened in the move commit 758242c on the plugin side, so this merge introduces zero path collisions and zero conflicts. Every pre-move commit retains its original SHA. The pre-merge tip is preserved as the tag 'liveobjects-plugin-pre-merge'. Known follow-ups required before the consolidated tree is functional: - The ably-common submodule that the plugin used for its tests is now registered only in merged-repos/.../.gitmodules. Git only consults the .gitmodules at the repo root, so the submodule needs to be re-registered in ably-cocoa's root .gitmodules. - The merged tree contains two Package.swift files (ably-cocoa's at root, plus the plugin's at merged-repos/.../Package.swift). The inner one is no longer used by SPM but is misleading; it should either be deleted or stubbed when the plugin's targets are folded into the root Package.swift. - The plugin's .github/workflows/check.yaml lands under merged-repos/... and will silently not run; CI needs unified. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every top-level type, extension, and free function in AblyLiveObjects gains an @available(macOS 10.15, iOS 13, tvOS 13, *) attribute. In the standalone ably-liveobjects-swift-plugin package the floor was macOS 11 / iOS 14 / tvOS 14, so these annotations were unnecessary. Once the target is folded into ably-cocoa's package, which declares macOS 10.11 / iOS 9 / tvOS 10, the Swift compiler needs an explicit @available on any declaration that uses a post-floor API (Swift Concurrency runtime, CryptoKit, Scanner's modern API, etc.). The chosen floor (10.15 / 13) matches what the APIs in question actually require — it is the lowest floor at which AblyLiveObjects can compile, and is one major version lower than the standalone plugin's previous floor. Generated mechanically by prepending @available to every line matching `(access-modifier)? (final )? (class|struct|enum|protocol |actor|extension|typealias|func)` at file scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps swift-tools-version from 5.3 to 6.1 (matches the version the former ably-liveobjects-swift-plugin Package.swift required) and restructures the manifest to fold both ex-external-packages in as internal targets: - Drops the package-level dependency on ably-cocoa-plugin-support. - Declares _AblyPluginSupportPrivate as an internal target whose path points into merged-repos/ably-cocoa-plugin-support/Sources/. The Ably target and the AblyTests test target now reference it by name rather than as a cross-package product. - Declares AblyLiveObjects as an internal Swift target whose path points into merged-repos/ably-liveobjects-swift-plugin/Sources/, with dependencies on the in-package Ably and _AblyPluginSupportPrivate targets. Exposes it as a separate .library product so consumers opt in to LiveObjects independently of Ably. What is NOT done in this commit, on purpose: - The former plugin's tests, BuildTool, Example app, and CI workflow remain in-tree at merged-repos/ but are not declared as targets. - The former plugin's transitive dependencies (swift-async-algorithms, swift-argument-parser, Table, swift-docc-plugin) are not declared here, since the AblyLiveObjects library target doesn't use them. - The package-wide `platforms:` floor stays at macOS 10.11 / iOS 9 / tvOS 10. AblyLiveObjects's higher API requirements are gated by @available in the previous commit, not by raising the floor. Verified: `swift build` and `swift build --build-tests` both succeed (warnings from pre-existing Swift 6 strict-concurrency checks remain). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the gitlink at merged-repos/ably-liveobjects-swift-plugin/Tests/AblyLiveObjectsTests/ably-common with a symlink to ably-cocoa's existing submodule at Test/AblyTests/ably-common. The plugin's pinned ably-common SHA (60fd9cf, Nov 2024) was an ancestor of ably-cocoa's pinned SHA (783496f, Sep 2025), so the ably-cocoa pin is strictly more recent. The plugin's tests will see the same test fixtures they had before, plus whatever was added in the ten months between the two pins. With this change there is a single ably-common submodule registered in the repo (still at the same root .gitmodules entry that ably-cocoa already had), instead of needing a second registration at the plugin test location. SPM's resources(.copy) follows the symlink at build time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same mechanical sweep as 45af634 applied to the test sources, with one adjustment: @available is intentionally omitted from declarations that are decorated with Swift Testing's @suite or @test attributes. The macro expansion conflicts with an adjacent explicit @available ("Attribute 'Suite' cannot be applied to this structure because it has been marked '@available …'"), so we rely on the macros' own availability handling for those declarations. In practice this is just the @Suite-decorated ObjectsIntegrationTests struct; the @Test-decorated free functions weren't matched by the sweep regex anyway because the @test attribute sits on its own line above them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a .testTarget for the plugin's existing test suite at merged-repos/ably-liveobjects-swift-plugin/Tests/AblyLiveObjectsTests/, with dependencies on AblyLiveObjects, Ably, and _AblyPluginSupportPrivate. Excludes the dotfile configs and CLAUDE.md; copies the ably-common directory (now a symlink to ably-cocoa's existing submodule) as a test resource. Sets swiftLanguageMode(.v5) on the pre-existing AblyTests and AblyTesting targets. Both contain code written under Swift 5 semantics: under Swift 6 language mode (the default once swift-tools-version is bumped to 6.1) the existing strict- concurrency warnings get promoted to errors. Pinning these two targets to .v5 preserves their previous behaviour while the new AblyLiveObjects/AblyLiveObjectsTests targets continue to build in Swift 6 mode, where they were written. Verified: `swift build --build-tests` clean from a fresh .build, and a smoke run of `swift test --filter InternalDefaultLiveCounterTests` passes (26 tests in 12 suites). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an .executableTarget for the developer/CI tool from the former plugin package, pointing at merged-repos/ably-liveobjects-swift-plugin/Sources/BuildTool/. Adds its three runtime dependencies at package level: - swift-argument-parser (for CLI option parsing) - swift-async-algorithms (used in process orchestration) - Table (terminal-table formatting) These are scoped to BuildTool only — they are not declared as target dependencies of the Ably or AblyLiveObjects library products, so they do not enter library consumers' build graphs. They are still added to Package.resolved, which means consumers fetch them at resolution time even though nothing they use compiles against them. `swift run BuildTool --help` works and lists all the previous subcommands. Known gap: BuildTool's `test-library`, `build-library`, `build-example-app`, and `build-documentation` subcommands all invoke xcodebuild with `-scheme AblyLiveObjects` and (for tests) `-testPlan UnitTests`. Those scheme/test-plan names exist only in the standalone plugin's Xcode workspace, not in ably-cocoa's Ably.xcodeproj. Wiring them up needs either an Xcode scheme addition (out of scope here) or a BuildTool refactor that calls `swift test` directly. Until then, the substitute for `swift run BuildTool test-library --platform macOS --only-unit-tests` is `swift test --filter AblyLiveObjectsTests --skip ObjectsIntegrationTests`, which passes 277 tests across 87 suites in this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Translates the SPM-only subset of the former plugin repo's
check.yaml into a new workflow at
.github/workflows/check-liveobjects.yaml at the actual root, where
GitHub Actions can see it. Two jobs:
- build-and-test-spm: builds the AblyLiveObjects target and runs
the unit tests via `swift test --filter AblyLiveObjectsTests
--skip ObjectsIntegrationTests`.
- build-release-configuration-spm: builds AblyLiveObjects with
--configuration release.
Both invocations were verified locally in this branch.
Also removes the original check.yaml from
merged-repos/ably-liveobjects-swift-plugin/.github/workflows/,
which lived under merged-repos/ and could never run (GitHub Actions
only reads root-level .github/workflows/). Its full original
contents remain reachable via the liveobjects-plugin-pre-merge tag.
What is NOT yet ported, with the reason in each case:
- lint: requires `swift run BuildTool lint`, which shells out to
`mint run swiftformat`, `mint run swiftlint`, and
`npm run prettier:check` from CWD. Running it from this repo's
root would walk into ably-cocoa's pure-ObjC source and fail.
Either BuildTool needs scoping to merged-repos/, or the lint
command needs to cd in. Out of scope here.
- generate-matrices: outputs an Xcode-version matrix for the
Xcode-based jobs below. Not useful until those jobs work.
- build-and-test-xcode, build-release-configuration-xcode,
code-coverage, check-example-app: all route through
`swift run BuildTool {build-library, test-library, …}` which
invokes `xcodebuild -scheme AblyLiveObjects -testPlan UnitTests`.
AblyLiveObjects has no Xcode scheme in ably-cocoa's
Ably.xcodeproj, and the test plans (UnitTests.xctestplan,
AllTests.xctestplan) under merged-repos/.../TestPlans/ aren't
wired into any project. Wiring needs either: an Xcode scheme
addition for AblyLiveObjects in Ably.xcodeproj, or a refactor
of BuildTool to call `swift test` directly.
- check-documentation: needs the swift-docc-plugin dependency
declared at the package level, plus the AWS-credentials and
sdk-upload-action setup parameterised for ably-cocoa rather
than ably-liveobjects-swift-plugin.
- all-checks-completed: trivial once the rest are restored;
just an aggregating job.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Adds a checked-in shared scheme at .swiftpm/xcode/xcshareddata/xcschemes/AblyLiveObjects.xcscheme so that Xcode (and xcodebuild via Ably.xcworkspace) sees a stable AblyLiveObjects scheme rather than relying on auto-generation, which produces a scheme without a configured test action. The TestPlans block references the two .xctestplan files that came in from the plugin, now living at merged-repos/ably-liveobjects-swift-plugin/TestPlans/. The AllTests test plan is the default; the UnitTests plan declares .integration as a skippedTag so that BuildTool's --only-unit-tests flag works through `xcodebuild -testPlan UnitTests`. Based on the plugin's previous in-repo scheme (merged-repos/.../.swiftpm/xcode/xcshareddata/xcschemes/AblyLiveObjects.xcscheme) with only the TestPlanReference paths updated to point into merged-repos/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XcodeRunner now prepends `-workspace Ably.xcworkspace` to every xcodebuild invocation. Without this, xcodebuild's auto-discovery picks Ably.xcodeproj at the repo root, which has no AblyLiveObjects scheme — the AblyLiveObjects (and BuildTool) schemes are SPM- generated and only visible when xcodebuild operates against the workspace. This is a behavioural change away from how BuildTool worked in the standalone ably-liveobjects-swift-plugin repo (no workspace there; xcodebuild auto-discovered the SPM package). Since BuildTool now lives only in this consolidated repo, hardcoding the workspace path is the right trade — making it configurable would just be yagni. With this change, `swift run BuildTool test-library --platform macOS --only-unit-tests` runs the unit suite via the UnitTests test plan (271 tests across 85 suites) and the other xcodebuild-routed subcommands (build-library, build-library-for-testing) succeed too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the brittle `swift test --skip ObjectsIntegrationTests`
invocation with `swift run BuildTool test-library --only-unit-tests`,
which selects tests via the UnitTests.xctestplan's `.integration`
skippedTag rather than by a hardcoded class name regex. Future
integration tests added with the .integration tag will be filtered
correctly regardless of their class name.
Adds two new jobs translated from the original plugin CI:
- build-and-test-xcode: builds for testing and runs the unit
suite via BuildTool for each of macOS, iOS, and tvOS.
- build-release-configuration-xcode: builds the library in
release configuration via BuildTool, also matrixed over the
three platforms.
Still not ported, with the same reasons as before (see commit
7ab9a1e): lint, code-coverage, check-example-app,
check-documentation, generate-matrices, and the all-checks-completed
aggregator. The first three depend on tooling that needs scoping
or extra dependencies; documentation needs swift-docc-plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The plugin's standalone repo could call its developer tool plainly
"BuildTool" because that repo contained only the LiveObjects code.
In the consolidated ably-cocoa package the name is misleading — it
implies a general-purpose build tool for the whole package, when in
fact every subcommand operates on the AblyLiveObjects target only
(test-library, build-library, build-example-app, build-documentation,
etc.).
Three rename touchpoints:
- Package.swift: executableTarget name "BuildTool"
-> "LiveObjectsBuildTool".
- BuildTool.swift: struct BuildTool -> LiveObjectsBuildTool, with
an explicit `commandName: "liveobjects-build-tool"` on the
CommandConfiguration so that the kebab-case help text stays
short rather than becoming "live-objects-build-tool" via
ArgumentParser's automatic derivation.
- .github/workflows/check-liveobjects.yaml: `swift run BuildTool`
-> `swift run LiveObjectsBuildTool` across the four invocations.
The source directory remains at
merged-repos/ably-liveobjects-swift-plugin/Sources/BuildTool/ —
renaming the directory would just churn `git log --follow` for no
real benefit. SPM target-name and directory-name are decoupled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renames the four jobs in check-liveobjects.yaml so that their IDs carry the liveobjects- prefix: - build-and-test-spm -> liveobjects-build-and-test-spm - build-release-configuration-spm -> liveobjects-build-release-configuration-spm - build-and-test-xcode -> liveobjects-build-and-test-xcode - build-release-configuration-xcode -> liveobjects-build-release-configuration-xcode In the consolidated repo there will eventually be CI jobs from several different feature areas (ably-cocoa core, LiveObjects, maybe plugin-support specifics). Without a prefix, generic IDs like "build-and-test-spm" collide conceptually with whatever other workflow files name their own SPM jobs, and the GitHub Actions UI shows them all interleaved. The prefix makes it immediately clear which subsystem each job belongs to. The human-readable `name:` strings are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.