Skip to content

Merge LiveObjects plugin and plugin-support into ably-cocoa - #2222

Merged
sacOO7 merged 19 commits into
integration/liveobjectsfrom
AIT-1027-integrate-liveobjects
Jul 24, 2026
Merged

Merge LiveObjects plugin and plugin-support into ably-cocoa#2222
sacOO7 merged 19 commits into
integration/liveobjectsfrom
AIT-1027-integrate-liveobjects

Conversation

@lawrence-forooghian

@lawrence-forooghian lawrence-forooghian commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Resolves AIT-1137 (split from AIT-1027): merges the contents of ably-liveobjects-swift-plugin (at a3bf63d) and ably-cocoa-plugin-support (at a290b89) into this repository, so that ably-cocoa and its plugins live in a single repo (as with ably-js and ably-java).

Background

ADR-128 placed the LiveObjects plugin in a separate repository on the belief that, because SPM platform requirements are package-wide, a plugin needing modern OS versions could not live in the same package as ably-cocoa and its much older platform floor. That belief turned out to be wrong: platform requirements set only the floor, and per-declaration @available annotations let individual components of a package require newer OS versions (with the compiler enforcing that consumers on older OS versions guard their use). That removes the reason for the separate repos, whose cost — every cross-cutting change (e.g. the UTS groundwork, AIT-1027) rippling across three repos and three PRs — motivated this merge.

Scope

The plugin is integrated as a largely self-contained directory (LiveObjects/) rather than being unified with the core SDK's tooling — the two keep their separate Xcode workspaces, lint setups (SwiftLint/SwiftFormat vs EditorConfig), CI workflows, test harnesses, example apps and dev docs. That's deliberate: unifying any of these would balloon the scope of an already-large change and destroy the verbatim-import provenance that makes it reviewable. The plugin-support layer (_AblyPluginSupportPrivate) is likewise kept exactly as it was, including machinery that arguably no longer needs to exist — e.g. the handshake by which the plugin and the core SDK check that they agree on the Realtime protocol version, which cannot diverge now that both sides live in the same repo and version. Note that one seemingly obvious simplification — dropping plugin-support entirely and having the plugin import Ably.Private (the Clang submodule that the test suite uses) — is a non-direction: as noted in ADR-128's "Alternatives considered", when this was tried, the private API appeared to be transitively exposed to the plugin's own users (internal import did not hide it). The repo merge is the enabler, not the end state — we can integrate further incrementally as pain points show up in the development workflow (candidates: a single workspace, shared lint tooling, folding the LiveObjects tests into a unified test setup, a combined example app).

Review commit by commit. The sequence is: commits 1–5 internalise plugin-support as an in-repo target (staying on tools 5.3); commits 6 onwards raise swift-tools-version to 6.1 and integrate LiveObjects. The two import commits are verbatim, script-generated copies of the source repos at a stated SHA (Scripts/import-repo-contents.sh); every deviation from verbatim is its own commit with the reasoning in its message. The @available annotation commit is entirely script-generated (Scripts/annotate-liveobjects-availability.py).

Key decisions

Full rationale is in the relevant commit messages:

  • ably-cocoa's declared platforms are unchanged; the LiveObjects module declares its newer OS requirements via @available on every top-level declaration (script-enforced in CI).
  • SPM consumers will require Xcode 16.3+ (tools 6.1). CocoaPods/Carthage consumers unaffected. See the tools-bump commit message for the consumer-impact analysis and the documented escape hatch (a version-specific-manifest retrofit) if this bites anyone.
  • _AblyPluginSupportPrivate becomes an internal target, deliberately not vended as a product.
  • LiveObjects is documented as experimental and exempt from semver while the Ably product keeps full guarantees.
  • One ably-common submodule (the plugin's is dropped; its pin was an ancestor of ours).

The branch is 19 commits, including the LiveObjects CI workflow (ported from the plugin repo, plus new availability-annotation enforcement jobs). Local verification of the full branch: all 290 LiveObjects tests pass (including sandbox integration tests) via both SPM and BuildTool/xcodebuild test plans, on macOS and tvOS simulator; example app builds; full lint passes; docs build via xcodebuild docbuild; -warnings-as-errors build clean.

One structural discovery from the workflow's first CI runs, fixed in the final two commits: xcodebuild compiles the test target at (near) the package's platform floor, the test sources cannot carry @available annotations (the swift-testing macros reject them), so BuildTool now raises the deployment target of test builds via build-setting overrides — see those commits' messages for the full story.

After merging

  1. Reopen the plugin repo's pending PRs against this repo: ably-liveobjects-swift-plugin#131 (ECO-5658 Switch to something simpler for subscription storage) and ably-liveobjects-swift-plugin#133 (Implement a couple of recent spec-clarity changes).
  2. Open ably-liveobjects-swift-plugin#134 ([AIT-1023] Path-based public API) as a new PR here, into a new feature/path-based-liveobjects integration branch that is branched from integration/liveobjects.

Release coordination

To be done before/alongside the first ably-cocoa release containing this:

  1. Cut a final release of the standalone plugin whose manifest caps its ably-cocoa dependency with an upper bound (..< <first-integrated-version>) and whose README carries a deprecation notice pointing here. Number it 0.4.1, so that users on from: "0.4.0" pick up the cap on a routine update, after which SwiftPM keeps resolving the last working pair (and, if they later deliberately bump ably-cocoa, they get a clear version conflict that names the plugin as the blocker).
  2. Quote the following in the migration notes (ably-cocoa's CHANGELOG entry and the archived plugin repo's README), so that searching for it finds the fix. It is what users who bump ably-cocoa without ever taking the capped plugin release will see — an immediate error at package-graph load (verified empirically):
    error: multiple packages ('ably-cocoa', 'ably-cocoa-plugin-support') declare targets with a conflicting name: '_AblyPluginSupportPrivate'; target names need to be unique across the package graph
    error: multiple packages ('ably-cocoa', 'ably-liveobjects-swift-plugin') declare targets with a conflicting name: 'AblyLiveObjects'; target names need to be unique across the package graph
    
    The fix being: replace the plugin package dependency with this package's AblyLiveObjects product; module name and API are unchanged.
  3. Archive ably-liveobjects-swift-plugin and ably-cocoa-plugin-support (transferring any open issues here). Their published releases remain resolvable indefinitely.
  4. Once this work lands on main, add this repo's LiveObjects workflow all-checks-completed job to main's branch-protection required status checks.
  5. Update the ably.com docs (both in the docs repo):
    • the LiveObjects Swift quickstart (src/pages/docs/liveobjects/quickstart/swift.mdx) installs the standalone ably-liveobjects-swift-plugin package; it should instead install ably-cocoa and select the AblyLiveObjects product;
    • the LiveObjects nav's Swift API-reference link (src/data/nav/liveobjects.ts) points at the generated docs' old location, https://sdk.ably.com/builds/ably/ably-liveobjects-swift-plugin/main/AblyLiveObjects/documentation/ablyliveobjects/; this workflow now publishes them at https://sdk.ably.com/builds/ably/ably-cocoa/main/AblyLiveObjects/documentation/ablyliveobjects/.

Release timing

This PR targets the integration/liveobjects branch rather than main. Releasing the repo move on its own would mean two disruptive changes for users of the current plugin (first switching to getting it from ably-cocoa, then adopting the path-based API later), so instead the repo move and the path-based API work will accumulate on the integration branch and be released to main together as a single release with a simpler narrative: "LiveObjects has a new API and now lives in ably-cocoa". Meanwhile main remains freely releasable for the core SDK. (This also means the docs updates above happen once, as part of documenting the new API.)

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 159 files, which is 9 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc6a4469-b69a-48ac-9df2-c6384587188f

📥 Commits

Reviewing files that changed from the base of the PR and between 0effa5c and b99c0af.

⛔ Files ignored due to path filters (3)
  • LiveObjects/images/SwiftSDK-LiveObjects-github.png is excluded by !**/*.png
  • LiveObjects/images/unit-tests-test-plan-screenshot.png is excluded by !**/*.png
  • LiveObjects/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (159)
  • .github/workflows/liveobjects.yaml
  • .swiftpm/xcode/xcshareddata/xcschemes/AblyLiveObjects.xcscheme
  • CHANGELOG.md
  • CLAUDE.md
  • Docs/plugins.md
  • LiveObjects/.gitignore
  • LiveObjects/.prettierignore
  • LiveObjects/.prettierrc
  • LiveObjects/.swift-version
  • LiveObjects/.swiftformat
  • LiveObjects/.swiftlint.yml
  • LiveObjects/AblyLiveObjects.xcworkspace/contents.xcworkspacedata
  • LiveObjects/AblyLiveObjects.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • LiveObjects/AblyLiveObjects.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • LiveObjects/BuildTool/.swiftpm/xcode/xcshareddata/xcschemes/BuildTool.xcscheme
  • LiveObjects/BuildTool/Package.resolved
  • LiveObjects/BuildTool/Package.swift
  • LiveObjects/BuildTool/Sources/BuildTool.swift
  • LiveObjects/BuildTool/Sources/Configuration.swift
  • LiveObjects/BuildTool/Sources/DestinationFetcher.swift
  • LiveObjects/BuildTool/Sources/DestinationPredicate.swift
  • LiveObjects/BuildTool/Sources/DestinationSpecifier.swift
  • LiveObjects/BuildTool/Sources/DestinationStrategy.swift
  • LiveObjects/BuildTool/Sources/Error.swift
  • LiveObjects/BuildTool/Sources/Platform.swift
  • LiveObjects/BuildTool/Sources/ProcessRunner.swift
  • LiveObjects/BuildTool/Sources/String+Decoding.swift
  • LiveObjects/BuildTool/Sources/XcodeRunner.swift
  • LiveObjects/CHANGELOG.md
  • LiveObjects/CLAUDE.md
  • LiveObjects/CONTRIBUTING.md
  • LiveObjects/Example/AblyLiveObjectsExample.xcodeproj/project.pbxproj
  • LiveObjects/Example/AblyLiveObjectsExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • LiveObjects/Example/AblyLiveObjectsExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • LiveObjects/Example/AblyLiveObjectsExample/AblyLiveObjectsExample.entitlements
  • LiveObjects/Example/AblyLiveObjectsExample/AblyLiveObjectsExampleApp.swift
  • LiveObjects/Example/AblyLiveObjectsExample/Assets.xcassets/AccentColor.colorset/Contents.json
  • LiveObjects/Example/AblyLiveObjectsExample/Assets.xcassets/AppIcon.appiconset/Contents.json
  • LiveObjects/Example/AblyLiveObjectsExample/Assets.xcassets/Contents.json
  • LiveObjects/Example/AblyLiveObjectsExample/ContentView.swift
  • LiveObjects/Example/AblyLiveObjectsExample/Helpers/ARTRealtimeChannel+Async.swift
  • LiveObjects/Example/AblyLiveObjectsExample/Preview Content/Preview Assets.xcassets/Contents.json
  • LiveObjects/Example/AblyLiveObjectsExample/Secrets.example.swift
  • LiveObjects/Example/AblyLiveObjectsExample/ViewModels/LiveCounterViewModel.swift
  • LiveObjects/Example/AblyLiveObjectsExample/ViewModels/TaskBoardViewModel.swift
  • LiveObjects/Example/AblyLiveObjectsExample/Views/LiveCounterView.swift
  • LiveObjects/Example/AblyLiveObjectsExample/Views/TaskBoardView.swift
  • LiveObjects/Mintfile
  • LiveObjects/README.md
  • LiveObjects/Sources/AblyLiveObjects/.swiftformat
  • LiveObjects/Sources/AblyLiveObjects/.swiftlint.yml
  • LiveObjects/Sources/AblyLiveObjects/Internal/ARTClientOptions+Objects.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/CoreSDK.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/DefaultInternalPlugin.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/DefaultLiveCounterUpdate.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/DefaultLiveMapUpdate.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/LiveObjectUpdate.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/ObjectCreationHelpers.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/ObjectDiffHelpers.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/ObjectsOperationSource.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/ObjectsSyncState.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/PublishResult.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/SimpleClock.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/SubscriptionStorage.swift
  • LiveObjects/Sources/AblyLiveObjects/Internal/SyncObjectsPool.swift
  • LiveObjects/Sources/AblyLiveObjects/Protocol/InboundObjectMessage+Synthetic.swift
  • LiveObjects/Sources/AblyLiveObjects/Protocol/ObjectMessage.swift
  • LiveObjects/Sources/AblyLiveObjects/Protocol/SyncCursor.swift
  • LiveObjects/Sources/AblyLiveObjects/Protocol/WireEnum.swift
  • LiveObjects/Sources/AblyLiveObjects/Protocol/WireObjectMessage.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/ARTRealtimeChannel+Objects.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Plugin.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveCounter.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultRealtimeObjects.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicObjectsStore.swift
  • LiveObjects/Sources/AblyLiveObjects/Public/PublicTypes.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/Assertions.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/Data+Extensions.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/Dictionary+Extensions.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/DispatchQueue+Extensions.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/DispatchQueueMutex.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/Errors.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/ExtendedJSONValue.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/JSONValue.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/Logger.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/LoggingUtilities.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/MarkerProtocolHelpers.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/NSLock+Extensions.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/WeakRef.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/WireCodable.swift
  • LiveObjects/Sources/AblyLiveObjects/Utility/WireValue.swift
  • LiveObjects/TestPlans/AllTests.xctestplan
  • LiveObjects/TestPlans/UnitTests.xctestplan
  • LiveObjects/Tests/AblyLiveObjectsTests/.swiftformat
  • LiveObjects/Tests/AblyLiveObjectsTests/.swiftlint.yml
  • LiveObjects/Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/CLAUDE.md
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/Ably+Concurrency.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/Assertions.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/ClientHelper.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/Sandbox.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/Subscriber.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/Tag+Integration.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Helpers/TestLogger.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/InternalDefaultLiveCounterTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsHelper.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/JS Integration Tests/TestProxyTransport.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/JSONValueTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/LiveObjectMutableStateTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Mocks/MockCoreSDK.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Mocks/MockLiveMapObjectsPoolDelegate.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Mocks/MockRealtimeObjects.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/Mocks/MockSimpleClock.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/ObjectDiffHelpersTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/ObjectLifetimesTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/ObjectMessageTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/ObjectsPoolTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/SyncCursorTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/SyncObjectsPoolTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/WireObjectMessageTests.swift
  • LiveObjects/Tests/AblyLiveObjectsTests/WireValueTests.swift
  • LiveObjects/package.json
  • Package.resolved
  • Package.swift
  • README.md
  • Scripts/annotate-liveobjects-availability.py
  • Sources/_AblyPluginSupportPrivate/APDependencyStore.m
  • Sources/_AblyPluginSupportPrivate/README.md
  • Sources/_AblyPluginSupportPrivate/include/APConnectionDetails.h
  • Sources/_AblyPluginSupportPrivate/include/APDecodingContext.h
  • Sources/_AblyPluginSupportPrivate/include/APDependencyStore.h
  • Sources/_AblyPluginSupportPrivate/include/APEncodingFormat.h
  • Sources/_AblyPluginSupportPrivate/include/APLiveObjectsPlugin.h
  • Sources/_AblyPluginSupportPrivate/include/APLogLevel.h
  • Sources/_AblyPluginSupportPrivate/include/APLogger.h
  • Sources/_AblyPluginSupportPrivate/include/APPluginAPI.h
  • Sources/_AblyPluginSupportPrivate/include/APPublicClientOptions.h
  • Sources/_AblyPluginSupportPrivate/include/APPublicErrorInfo.h
  • Sources/_AblyPluginSupportPrivate/include/APPublicRealtimeChannel.h
  • Sources/_AblyPluginSupportPrivate/include/APPublicRealtimeChannelUnderlyingObjects.h
  • Sources/_AblyPluginSupportPrivate/include/APPublishResult.h
  • Sources/_AblyPluginSupportPrivate/include/APRealtimeChannel.h
  • Sources/_AblyPluginSupportPrivate/include/APRealtimeChannelState.h
  • Sources/_AblyPluginSupportPrivate/include/APRealtimeClient.h

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch AIT-1027-integrate-liveobjects

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

Comment @coderabbitai help to get the list of available commands.

@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 02:39 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 02:41 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 02:41 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 10:03 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/AblyLiveObjects July 10, 2026 10:04 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 10:07 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 10:07 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 10:22 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/AblyLiveObjects July 10, 2026 10:23 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 10:26 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 10:26 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 10:29 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/AblyLiveObjects July 10, 2026 10:30 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 10:33 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 10:33 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 11:56 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/AblyLiveObjects July 10, 2026 11:57 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 12:01 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 12:01 Inactive
@lawrence-forooghian
lawrence-forooghian marked this pull request as ready for review July 10, 2026 12:51
@lawrence-forooghian

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/features July 10, 2026 14:47 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/AblyLiveObjects July 10, 2026 14:49 Inactive
Comment thread Docs/plugins.md Outdated
Comment thread LiveObjects/CONTRIBUTING.md Outdated
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/jazzydoc July 10, 2026 14:52 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2222/markdown-api-reference July 10, 2026 14:52 Inactive
Comment thread README.md Outdated
lawrence-forooghian and others added 18 commits July 10, 2026 18:39
This is the verbatim contents of the ably-cocoa-plugin-support repo [1]
at commit a290b89, imported by running:

    Scripts/import-repo-contents.sh \
        https://github.com/ably/ably-cocoa-plugin-support.git \
        a290b89 \
        PluginSupport

No modifications have been made to the imported files; subsequent
commits will integrate them into this repo.

This is part of merging the plugin-support and LiveObjects repos into
ably-cocoa (AIT-1027), so that there is a single repository for
ably-cocoa and its plugins. The _AblyPluginSupportPrivate library,
which exposes ably-cocoa private API to Ably-authored plugins, only
needed to live in a separate repo while its consumers (ably-cocoa and
the LiveObjects plugin) lived in separate repos; it now becomes an
implementation detail of this repo.

[1] https://github.com/ably/ably-cocoa-plugin-support

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure renames, no content changes:

- PluginSupport/Sources/_AblyPluginSupportPrivate
    -> Sources/_AblyPluginSupportPrivate
- PluginSupport/README.md
    -> Sources/_AblyPluginSupportPrivate/README.md

This is where the _AblyPluginSupportPrivate target will live as an
ordinary target of the ably-cocoa package (a subsequent commit updates
Package.swift). The README will be revised for its new home in a later
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes the remaining files imported from the ably-cocoa-plugin-support
repo, which are superseded now that its source code is part of this
repo:

- PluginSupport/Package.swift: the library becomes a target of this
  package (next commit) rather than a package of its own
- PluginSupport/LICENSE, PluginSupport/COPYRIGHT: this repo's LICENSE
  and COPYRIGHT (identical Apache 2.0 licence) now cover the code
- PluginSupport/.gitignore: only ignored build artefacts of the
  standalone package (.build etc.), which this repo's .gitignore
  already covers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the ably-cocoa-plugin-support package dependency with a
target built from the sources imported in the preceding commits. The
target is deliberately not vended as a product: it only ever existed
to expose ably-cocoa private API to Ably-authored plugins, and its
only consumers (ably-cocoa and the LiveObjects plugin, which a later
commit brings into this repo) now live here.

Note for the transition period: a dependency graph that contains both
this version of ably-cocoa and the standalone LiveObjects plugin
(which depends on the ably-cocoa-plugin-support package) will fail to
resolve with a duplicate target name error, since both provide a
target named _AblyPluginSupportPrivate. That combination could never
work anyway; plugin users should migrate to the AblyLiveObjects
product introduced later in this branch. A final release of the
standalone plugin will cap its ably-cocoa dependency so that existing
users' `swift package update` keeps resolving a working (old) pair.

CocoaPods and Carthage are unaffected: plugin support is SPM-only
(ABLY_SUPPORTS_PLUGINS is only defined in Package.swift), and neither
the podspec nor the Xcode project referenced the removed package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Needed so that the AblyLiveObjects target (added in a later commit)
can use the Swift 6 language mode and internal imports, which require
per-target .swiftLanguageMode settings (tools 6.0+).

Consequences for users:

- SPM consumers now need Xcode 16.3 or later (April 2025) to resolve
  this package. CocoaPods and Carthage consumers are unaffected
  (neither reads Package.swift).
- This does not change the OS versions the SDK supports: the declared
  platforms are unchanged, and deployment-target support is a property
  of the consumer's Xcode, not of the tools version. Since 28 April
  2026 the App Store requires apps to be built with Xcode 26 [1], so
  in practice App Store developers already exceed this requirement.
- Consumers on older toolchains resolving with a `from:` range will
  stay pinned to the last tools-5.3 release (1.2.x) rather than
  failing, since SwiftPM excludes versions whose manifest requires
  newer tools.

Escape hatch: if consumers who cannot upgrade Xcode (e.g. enterprise
certificate distribution) report breakage, this is fixable in a patch
release by renaming this manifest to Package@swift-6.1.swift and
reintroducing a tools-5.3 Package.swift vending only the Ably product;
SwiftPM serves each consumer the newest manifest their tools support.

Mechanical changes required by the new tools version:

- Test targets that predate the Swift 6 language mode (AblyTests,
  AblyTesting) explicitly opt in to .swiftLanguageMode(.v5), since
  tools 6.x defaults targets to the Swift 6 mode.
- The UTS test target's unsafeFlags(["-swift-version", "6"])
  workaround becomes a proper .swiftLanguageMode(.v6).
- The deprecated .package(name:url:from:) dependency forms become
  .package(url:from:), which in turn requires the explicit
  .product(name:package:) form for cross-package target dependencies.

[1] https://developer.apple.com/news/upcoming-requirements/?id=02032026a

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This is the verbatim contents of the ably-liveobjects-swift-plugin
repo [1] at commit a3bf63d, imported by running:

    Scripts/import-repo-contents.sh \
        https://github.com/ably/ably-liveobjects-swift-plugin.git \
        a3bf63d697ebf8b1c0da6d6491df504728fa4c03 \
        LiveObjects

No modifications have been made to the imported files; subsequent
commits integrate them into this repo. Two notes on the import
mechanics:

- The plugin repo's Tests/AblyLiveObjectsTests/ably-common submodule
  is not imported (git archive emits submodules as empty directories).
  A later commit points the affected test helper at this repo's
  existing ably-common submodule instead.
- If a newer version of the plugin needs to be swapped in before this
  branch lands, drop this commit, rerun the script at the new SHA and
  recommit; the subsequent commits are either outside LiveObjects/,
  small itemised deletions, or regenerated by script.

This is the main step of merging the LiveObjects plugin into
ably-cocoa (AIT-1027), giving a single repository for ably-cocoa and
its plugins, as with ably-js and ably-java. LiveObjects/ is the
permanent home of this code, not a staging area: the plugin's
development tooling, example app, docs and configuration continue to
live and work in this directory.

[1] https://github.com/ably/ably-liveobjects-swift-plugin

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two preceding import commits are the only reason
Scripts/import-repo-contents.sh existed, so remove it now that they
are done; this repo has nothing left to import. The script remains
available in the history at the import commits (whose messages
reference it and describe how to rerun it), so the imports remain
reproducible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes files imported from the ably-liveobjects-swift-plugin repo
that are superseded by their ably-cocoa equivalents:

- LiveObjects/Package.swift, LiveObjects/Package.resolved: the
  AblyLiveObjects library becomes a product of this package (a later
  commit adds its targets to the root manifest); the BuildTool target
  gets its own nested package in a later commit
- LiveObjects/.github/: the plugin's CI workflow; a later commit adds
  an equivalent workflow to this repo's .github/workflows
- LiveObjects/.gitmodules: git only honours the repository-root
  .gitmodules, so this imported copy is inert; the submodule it
  described is not imported (see previous commit)
- LiveObjects/LICENSE, LiveObjects/COPYRIGHT, LiveObjects/
  MAINTAINERS.md: this repo's equivalents (identical Apache 2.0
  licence) now cover the code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves the plugin's BuildTool executable (imported two commits ago as
LiveObjects/Sources/BuildTool) into a standalone package at
LiveObjects/BuildTool, with a new manifest carrying over its
dependencies (swift-argument-parser, swift-async-algorithms, Table)
from the deleted plugin manifest. Its Xcode scheme moves alongside it.

Making it a separate package, rather than a target of the root
ably-cocoa package, keeps its dependencies out of the manifest that
ably-cocoa's consumers resolve. It is run from the LiveObjects
directory:

    swift run --package-path BuildTool BuildTool <subcommand>

The move of the sources is a pure git rename; a later commit fixes the
path assumptions that no longer hold in its new home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds Scripts/annotate-liveobjects-availability.py and applies it,
inserting

    @available(macOS 11.0, iOS 14.0, tvOS 14.0, *)

above all 222 top-level declarations in
LiveObjects/Sources/AblyLiveObjects. These are the platform versions
that the standalone plugin declared in its Package.swift.

This change is entirely script-generated, with no manual edits, so it
can be regenerated if the imported plugin sources are ever replaced
with a newer version (see the import commit).

Why this is needed: SwiftPM platform requirements are package-wide,
and ably-cocoa's (iOS 9 / macOS 10.11 / tvOS 10) are far below what
LiveObjects requires — its Swift concurrency usage alone needs
iOS 13 / macOS 10.15. Per-declaration availability is how a single
package hosts both: the compiler builds AblyLiveObjects at the
package's old deployment target and these annotations both satisfy
its availability checking and make consumers on older OS versions
guard their use of the LiveObjects API with #available.

The script is idempotent; CI will run it and fail on a non-empty diff
so that new declarations cannot be added without an annotation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plugin repo had its own ably-common submodule, used for exactly
one thing: the sandbox test helper reads test-app-setup.json from it
when creating an Ably sandbox app for the integration tests. This repo
already has an ably-common submodule (at Test/AblyTests/ably-common,
used by AblyTests), and we do not want two.

The helper now resolves the file via #filePath relative to the repo
layout, instead of a bundle resource, because:

- SwiftPM requires a target's resources to live inside the target's
  directory, and the submodule lives in another target's tree.
- Symlinking it into this target's tree does not work: SwiftPM copies
  the symlink itself into the resource bundle, where it dangles.

Note that the two repos' submodules pointed at different ably-common
commits; this repo's pin is a descendant of the plugin's, so this is a
fast-forward for the fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the AblyLiveObjects target (the sources imported from the
ably-liveobjects-swift-plugin repo), its test target, and a library
product, to the root manifest. The target paths point into
LiveObjects/.

Users of the standalone plugin migrate by replacing their
ably-liveobjects-swift-plugin package dependency with this package's
AblyLiveObjects product; the module name and API are unchanged.

The test target has no resources entry: the fixture that was a bundle
resource in the plugin repo is loaded from the repo tree instead (see
previous commit). Package.resolved is unchanged, since the LiveObjects
library brings no new dependencies (its BuildTool development tool,
which does have dependencies, is deliberately a separate nested
package).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plugin's Xcode workspace and shared scheme assumed the package
manifest lived at the plugin repo root; the AblyLiveObjects targets
are now part of the ably-cocoa package at this repo's root. So:

- AblyLiveObjects.xcworkspace's package reference changes from
  "group:" to "group:..".
- The shared AblyLiveObjects scheme (which carries the test action and
  its test-plan associations, used by BuildTool's xcodebuild
  invocations) moves to this repo's .swiftpm scheme directory — Xcode
  only discovers package schemes at the package root — with its
  test-plan references updated to container:LiveObjects/TestPlans/.
- The AblyLiveObjects-Package scheme and the standalone package's
  workspace settings are removed as obsolete (the equivalent
  whole-package scheme now belongs to ably-cocoa).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The example app project declared a remote package dependency on
swift-async-algorithms, but nothing in its code imports it. In the
plugin repo this was harmless: BuildTool (a target of the plugin
package) also depended on swift-async-algorithms, so it was part of
the package's dependency graph anyway. In this repo, where BuildTool
is a separate nested package, the example app's reference would be the
only thing pulling swift-async-algorithms into the LiveObjects
workspace's resolution, making that resolution a superset of the root
package's dependency graph — which both defeats the lockfile
consistency check that a later commit preserves, and causes Xcode to
transiently rewrite the root Package.resolved with the extra pins.

Also removes the example project's own workspace-scoped
Package.resolved, whose only pins were this removed dependency and its
transitive dependency.

Verified by resolving the LiveObjects workspace (its resolution now
matches the root package's dependency graph exactly) and building the
example app for macOS via BuildTool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BuildTool and the lint configuration assumed the LiveObjects directory
was a repo/package root. It is still the working directory that
BuildTool must be run from, but some of the things it deals with now
live elsewhere:

- Lint's swift-tools-version consistency check now reads the root
  ../Package.swift (which is what governs compilation of the
  LiveObjects sources); .swift-version stays in LiveObjects/ because
  SwiftFormat reads it from there.
- Lint's lockfile comparison now compares the Xcode-managed workspace
  lockfile against the root ../Package.resolved. To make the two
  comparable (and to stop SwiftPM and Xcode disagreeing about the
  file's representation), the root Package.resolved is rewritten in
  the current lockfile format (version 3) with unchanged pins, and the
  workspace copy imported from the plugin repo is replaced with the
  Xcode-written equivalent for this package's dependency graph.
- SwiftLint and Prettier now exclude BuildTool/.build, the nested
  BuildTool package's build directory, which contains dependency
  checkouts (previously the build directory was .build, which the
  configs already exclude).
- The Secrets.swift file that BuildTool generates for example-app CI
  builds now ends with a newline, without which SwiftFormat fails a
  subsequent local lint run.

Verified by running every lint step and BuildTool's generate-matrices,
test-library and build-example-app subcommands against this layout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
xcodebuild compiles the AblyLiveObjectsTests target at (nearly) this
package's declared platform floor: for iOS and tvOS destinations it
raises the floor of 12 only as far as 13. The test code was written
against the standalone plugin repo's floor of iOS/tvOS 14 — its
xcodebuild test builds compiled at exactly that — and fails
availability checking below it, since it freely uses the LiveObjects
API, whose declarations are @available(iOS 14, tvOS 14, ...). The
plugin repo never hit this, and `swift test` doesn't either (SwiftPM
raises test targets' deployment target on macOS of its own accord),
which is why it only surfaced in this repo's Xcode-based CI jobs.

The test sources cannot simply be annotated the way the library
sources are: the swift-testing @suite macro rejects types marked
@available ("Attribute 'Suite' cannot be applied to this structure
because it has been marked '@available(...)'").

So instead, BuildTool now passes deployment-target build-setting
overrides of iOS/tvOS 14 — restoring the regime the test code was
written for — to the xcodebuild invocations that compile or run the
tests. Command-line build settings do apply to Swift package targets.
Test code that needs newer OS versions than this continues to declare
it with @available on the affected declarations (e.g. Subscriber.swift
and the tests that use it, whose parameter packs in generic types need
iOS/tvOS 17); the library's declared availability is still checked at
the package floor by the SPM-based CI jobs.

There is deliberately no macOS override: xcodebuild raises macOS
test-bundle builds well above the package floor of its own accord (the
test host has to run on the build machine), and the test code relies
on this — Subscriber.swift's @available annotation names only iOS and
tvOS, so its use of parameter packs (which need macOS 14) is unguarded
on macOS.

Verified by building and running the unit tests via BuildTool for tvOS
simulator and macOS destinations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the ably-liveobjects-swift-plugin repo's Check workflow as
.github/workflows/liveobjects.yaml, with the following adaptations for
its new home (the core SDK's existing workflows are untouched; the
check-spm workflow now inherently also builds the AblyLiveObjects
product):

- Steps run with LiveObjects/ as the working directory, and BuildTool
  is invoked via `swift run --package-path BuildTool BuildTool`. (The
  all-checks-completed marker job, which runs on ubuntu-latest without
  a checkout, overrides this default.)
- The SPM job's `swift test` is filtered to the AblyLiveObjectsTests
  module; the rest of the package's tests belong to the core SDK's own
  workflows (though `swift test` builds all of the package's test
  targets, so -warnings-as-errors covers their compilation here too —
  note that the warning set is a property of this job's Xcode version,
  and newer toolchains may warn about things this one does not).
- The docs job uses `xcodebuild docbuild` plus
  `docc process-archive transform-for-static-hosting` instead of the
  swift-docc-plugin package plugin, because the plugin would have to
  be a dependency of the root manifest, where ably-cocoa's consumers
  would resolve it. Uploads use this repo's AWS role.
- New check-availability-annotations job enforcing the @available
  annotations on the LiveObjects sources (see
  Scripts/annotate-liveobjects-availability.py): the compiler checks
  public declarations via -require-explicit-availability, and the
  annotation script (which is idempotent) checks all top-level
  declarations by requiring a clean diff after running it.
  (-require-explicit-availability must be spelled as a frontend flag;
  the driver flag of the same name is a no-op.)

The plugin repo's commented-out spec-coverage job has not been ported;
it can be revisited when spec coverage tooling is picked up again.

Note for branch protection: the all-checks-completed marker job of
this workflow should be added to the required status checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Root README: new LiveObjects section, including the experimental
  status note: breaking changes to the AblyLiveObjects API may occur
  in minor or patch releases without a major version bump, and
  ably-cocoa's semantic versioning guarantees apply only to the Ably
  product.
- CHANGELOG: unreleased entry describing the migration from the
  standalone plugin package and the new Xcode 16.3+ requirement for
  SPM consumers, restating the semver exemption.
- LiveObjects/README: the experimental warning gains the same semver
  wording; installation and releases now point at ably-cocoa; a note
  records the move from the ably-liveobjects-swift-plugin repo.
- LiveObjects/CONTRIBUTING: commands updated for the new layout; the
  "developing ably-cocoa alongside this plugin" section is removed
  (the code now lives in the same repo and package, so there is
  nothing for it to say); the standalone release process is replaced
  by a pointer to ably-cocoa's.
- LiveObjects/CLAUDE.md and root CLAUDE.md: build/test/lint commands
  updated for the new layout, and cross-references added.
- Docs/plugins.md: _AblyPluginSupportPrivate and the LiveObjects
  plugin are now described at their in-repo locations, and ADR-128 is
  described as the original design, noting that its central premise
  for a separate plugin repository (package-wide OS requirements)
  turned out to be wrong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@maratal maratal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Now we have root /Source (core SDK), root /Sources for _AblyPluginSupportPrivate and LiveObjects/Sources/AblyLiveObjects for plugin. It should be unified as root _AblyPluginSupportPrivate/Source and Plugins/AblyLiveObjects/Source (and maybe replace "Source" with "Sources" everywhere).

Now I'm thinking that keeping all the plugins in the main repo is probably not the best idea, we could leave _AblyPluginSupportPrivate in the main sdk repo and make plugin repo depending on it and importing test harness from it as well. WDYT @lawrence-forooghian @ttypic @sacOO7 (Lawrence, I believe you had this variant in mind too, why you still decided to merge everything into one?)

xcode-version: 16.4

- id: generation-step
run: swift run --package-path BuildTool BuildTool generate-matrices >> $GITHUB_OUTPUT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere: BuildTool BuildTool looks not very convenient, why can't stay just one BuildTool?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@maratal if you think you can make a PR to fix some of those inconsistencies, can you accordingly make the PR on top of current one to fix it?

@sacOO7 sacOO7 Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ideally, we want this PR to get merged by end of this week

@maratal maratal Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the plugin repo (ably-liveobjects-swift-plugin) there was only one SwiftPM package — the root one. BuildTool was just an .executableTarget of that root package (sources at Sources/BuildTool/, no separate Package.swift). CI ran from the repo root, so swift run BuildTool lint resolved the root package implicitly and ran its BuildTool target — no --package-path needed, because the package it wanted was the current directory.

During the merge into ably-cocoa, BuildTool was extracted into its own standalone package at LiveObjects/BuildTool/ (its own Package.swift), deliberately, so its dependencies (swift-argument-parser, async-algorithms, Table) aren't resolved by consumers of ably-cocoa. Now that it's a separate package in a subdirectory, swift run has to be pointed at it with --package-path BuildTool, while the trailing BuildTool is still the executable-target name.

So the second BuildTool isn't new and nothing was renamed — the executable was always called BuildTool. What's new is the --package-path BuildTool in front of it, forced by the target→separate-package split.

@maratal
maratal self-requested a review July 23, 2026 21:06

@maratal maratal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM with follow up PR - #2228 @sacOO7

@sacOO7

sacOO7 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

LGTM with follow up PR - #2228 @sacOO7

Great, I will merge this PR, since it's approved 👍

@sacOO7
sacOO7 merged commit ba97783 into integration/liveobjects Jul 24, 2026
44 of 45 checks passed
@lawrence-forooghian

Copy link
Copy Markdown
Contributor Author

Now I'm thinking that keeping all the plugins in the main repo is probably not the best idea, we could leave _AblyPluginSupportPrivate in the main sdk repo and make plugin repo depending on it

this is how things used to be — see 9e172dd for why we couldn't keep plugin-support in ably-cocoa

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