Skip to content

[Feature] Path based LiveObjects Implementation - #2230

Draft
sacOO7 wants to merge 25 commits into
mainfrom
integration/liveobjects
Draft

[Feature] Path based LiveObjects Implementation#2230
sacOO7 wants to merge 25 commits into
mainfrom
integration/liveobjects

Conversation

@sacOO7

@sacOO7 sacOO7 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

lawrence-forooghian and others added 23 commits July 10, 2026 18:39
Adds Scripts/import-repo-contents.sh, which extracts the tracked files
of another git repository at a given commit into a directory of this
repository.

This will be used by upcoming commits that import the contents of the
ably-cocoa-plugin-support and ably-liveobjects-swift-plugin repos into
this repo (see AIT-1027). Putting the import behind a script keeps
those commits reproducible: to swap in a newer version of an imported
repo, drop the import commit, rerun the script at the new SHA, and
recommit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
LiveObjects-only extraction of the path-based public API work
(alternative to #2225, with the unrelated UTS test-infra and tooling
changes dropped):

- Namespace the internal ObjectMessage wire types under ProtocolTypes,
  freeing their plain names for the public API
- Add the new path-based public surface (PathObject, Instance,
  RealtimeObject, value types, subscriptions) with Default* skeletons
- Remove the old channel.objects proxy objects, PublicTypes, and the
  ported JS integration tests
- Adapt the example app view models to the new API
The core SDK's sources live in Source/ (singular) while
_AblyPluginSupportPrivate lived in Sources/ (plural), so both a Source
and a Sources directory sat at the repo root — confusingly similar. This
moves the _AblyPluginSupportPrivate target's directory up to the repo
root, removing the Sources/ directory entirely so only Source/ remains.

_AblyPluginSupportPrivate is an SPM-only target referenced by name, so
the only path references are the target's own path: in Package.swift and
one mention in Docs/plugins.md; both are updated here. No CocoaPods,
Carthage, or Xcode-project changes are involved.

A follow-up will move both _AblyPluginSupportPrivate and the core SDK to
the idiomatic SwiftPM Sources/<TargetName>/ layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move _AblyPluginSupportPrivate out of `Sources/` to the repo root
Merge LiveObjects plugin and plugin-support into ably-cocoa
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 380b7d31-1937-4ce7-8426-79c42ea21e9d

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 integration/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/2230/features July 24, 2026 11:45 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/AblyLiveObjects July 24, 2026 11:45 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/jazzydoc July 24, 2026 11:49 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/markdown-api-reference July 24, 2026 11:49 Inactive
…lic-api

Add path-based LiveObjects public API
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/features July 28, 2026 10:12 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/AblyLiveObjects July 28, 2026 10:13 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/jazzydoc July 28, 2026 10:16 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2230/markdown-api-reference July 28, 2026 10:16 Inactive
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