Uh oh!
There was an error while loading. Please reload this page.
docs(liveobjects): migrate Swift documentation to the path-based API - #3515
docs(liveobjects): migrate Swift documentation to the path-based API#3515sacOO7 wants to merge 3 commits into
Conversation
Replaces the deprecated callback-based Swift API (channel.objects, getRoot(), live LiveMap/LiveCounter handles) with the path-based typed API (channel.object.get(), PathObject/Instance layers) across all LiveObjects pages, mirroring the Java migration in #3462. Every Swift snippet compiles against ably-cocoa's AblyLiveObjects product; JS/Java content is unchanged except language-gate widenings.
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
There was a problem hiding this comment.
Pull request overview
This PR migrates the Swift LiveObjects documentation from the deprecated callback-based/root-handle API to the new path-based typed API (channel.object.get(), PathObject/Instance layers, blueprints via LiveMap.create / LiveCounter.create), aligning Swift with the existing Java/JavaScript documentation structure.
Changes:
- Rewrites the Swift quickstart and updates concept pages to document the path layer + instance layer model, including async guidance and subscription patterns.
- Adds Swift code tabs across LiveMap/LiveCounter/typing/lifecycle/inband objects pages, and removes old Swift-only API sections.
- Updates supporting configuration (Swift SDK link in nav; LiveObjects Swift version in language data; channel accessor docs).
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/docs/liveobjects/typing.mdx | Adds Swift “Type inference” section and examples for path- vs instance-layer typing. |
| src/pages/docs/liveobjects/storage.mdx | Updates storage wording to reference the channel object consistently. |
| src/pages/docs/liveobjects/quickstart/swift.mdx | Rewrites Swift quickstart around channel.object.get() and blueprint-based creation/updates/subscriptions. |
| src/pages/docs/liveobjects/map.mdx | Adds Swift path-based examples throughout LiveMap usage and removes old Swift API tail. |
| src/pages/docs/liveobjects/lifecycle.mdx | Updates Swift lifecycle/sync/deletion docs to the path/instance subscription model. |
| src/pages/docs/liveobjects/index.mdx | Updates Swift availability note for batch operations. |
| src/pages/docs/liveobjects/inband-objects.mdx | Adds Swift examples for enabling and consuming inband [meta]objects messages. |
| src/pages/docs/liveobjects/counter.mdx | Adds Swift path-based examples throughout LiveCounter usage and removes old Swift API tail. |
| src/pages/docs/liveobjects/concepts/synchronization.mdx | Aligns Swift sync notes with “awaited mutation” semantics. |
| src/pages/docs/liveobjects/concepts/path-object.mdx | Adds Swift coverage for PathObject navigation, typing, compact snapshots, and subscriptions. |
| src/pages/docs/liveobjects/concepts/operations.mdx | Extends operations docs/tables with Swift types and examples using the new model. |
| src/pages/docs/liveobjects/concepts/objects.mdx | Migrates Swift examples to channel-object/path-based API and updates conceptual sections accordingly. |
| src/pages/docs/liveobjects/concepts/instance.mdx | Adds Swift Instance enum guidance and examples for navigation, typing, compact snapshots, and subscriptions. |
| src/pages/docs/liveobjects/batch.mdx | Updates Swift note to “not supported” with REST API guidance. |
| src/pages/docs/api/realtime-sdk/channels.mdx | Documents Swift channel.object accessor (plugin-provided) and removes old Swift objects entry. |
| src/data/nav/liveobjects.ts | Updates Swift SDK DocC link target/label (with TODO to verify). |
| src/data/languages/languageData.ts | Bumps LiveObjects Swift version displayed in language selector. |
Suppressed comments (17)
src/pages/docs/liveobjects/concepts/instance.mdx:105
instance()returns an Optional, so thisif caseneeds?to match.some(...). As written it won’t compile againstInstance?.
if case .liveCounter(let visits) = visitsInstance {
src/pages/docs/liveobjects/concepts/instance.mdx:261
instance()returns an Optional; theif caseneeds to handle the Optional result by adding?(or using.some(...)).
if case .liveMap(let settings) = try rootObject.get(key: "settings").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:203
instance()returns an Optional; add?to pattern-match the Optional result. Otherwise this example won’t compile.
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:328
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let user) = try rootObject.get(key: "user").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:337
get(key:)returns an Optional when the entry is missing, so thisif caseneeds?to match the Optional.
if case .liveCounter(let visits) = try user.get(key: "visits") {
src/pages/docs/liveobjects/concepts/instance.mdx:384
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let user) = try rootObject.get(key: "user").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:480
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let settings) = try rootObject.get(key: "settings").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:545
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let settings) = try rootObject.get(key: "settings").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:716
- Even though the channel object always exists,
instance()is still an Optional-returning API per the rest of the page. Thisif caseshould unwrap the Optional with?to compile.
if case .liveMap(let rootInstance) = try rootObject.instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:766
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let settings) = try rootObject.get(key: "settings").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:772
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:841
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:888
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:940
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:1004
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveCounter(let visits) = try rootObject.get(key: "visits").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:1061
instance()returns an Optional; add?so the pattern matches.some(...).
if case .liveMap(let settings) = try rootObject.get(key: "settings").instance() {
src/pages/docs/liveobjects/concepts/instance.mdx:1072
get(key:)returns an Optional when the entry is missing, so thisif caseneeds?to match the Optional.
if case .liveMap(let preferences) = try settings.get(key: "preferences") {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // A typed read returns the value, or nil when the path is missing | ||
| // or holds a value of a different type | ||
| let theme = try rootObject.at(path: "settings.theme").asPrimitive().value()?.stringValue // String or nil |
There was a problem hiding this comment.
No change needed here: try covers the whole expression, and the throwing call in this chain is value() (throws(ARTErrorInfo)) — at(path:) is indeed non-throwing navigation. Swift requires marking the expression that contains a throwing call, so the line is correct as written (and compiles in our snippet harness against ably-cocoa).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| try await visits.increment(amount: 1) | ||
| // A read through the wrong inferred type returns nil instead of throwing | ||
| let theme = try rootObject.at(path: "settings.theme").asPrimitive().value()?.stringValue // String or nil |
There was a problem hiding this comment.
No change needed here: try covers the whole expression, and the throwing call in this chain is value() (throws(ARTErrorInfo)) — at(path:) is indeed non-throwing navigation. Swift requires marking the expression that contains a throwing call, so the line is correct as written (and compiles in our snippet harness against ably-cocoa).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
rest-api-usage#updating-objects-by-id -> #update-by-id and rest-api-usage#fetching-objects-list-values -> #fetching-objects; neither anchor exists on the target page. Found during the Swift migration link audit; affects all languages.
…amples; grammar fix Normalize 33 bare enum-case patterns matching Optional scrutinees (instance()/get(key:) results) to the explicit '...(let x)? =' form, for consistency with the counter/objects/operations/path-object pages and to make the optionality visible to readers. The bare form compiled as written (Swift matches enum-case patterns through Optionals), so this is a readability/consistency change, not a correctness fix. Four bare patterns are deliberately kept where the scrutinee is non-optional (if-let-unwrapped values and event.object), where adding '?' would not compile. All edited fences re-verified with swift build against ably-cocoa. Also fixes subject-verb agreement in the shared batch-operations sentence on the index page (addresses Copilot review on #3515).
| liveObjects: { | ||
| javascript: '2.21', | ||
| swift: '0.4', | ||
| swift: '1.2', |
There was a problem hiding this comment.
| swift: '1.2', | |
| swift: '1.3', |
Is this supposed to be 1.3 since we are introducing new plugin API to cocoa library.
cc @lawrence-forooghian@maratal
Summary
Migrates the Swift LiveObjects documentation from the deprecated callback-based API to the new path-based typed API, bringing the Swift content in line with the JavaScript and Java path-based docs (#3462).
The old Swift surface (
channel.objects,getRoot(),createMap()/createCounter(), liveLiveMap/LiveCounterhandles,.on(event: .deleted)) is replaced throughout with the current API shipped in ably-cocoa'sAblyLiveObjectsproduct:channel.objects→RealtimeObjectschannel.object(extension property,any RealtimeObject)try await channel.objects.getRoot()try await channel.object.get()→any LiveMapPathObjectobjects.createMap()/createCounter()LiveMap.create(entries:)/LiveCounter.create(initialCount:)blueprints, assigned viaset(key:value:)LiveMap/LiveCounterhandlesLiveMapPathObject/LiveCounterPathObject) + instance layer (LiveMapInstance/LiveCounterInstance)counter.on(event: .deleted)operation.action == .objectDelete)What changed
Getting started
quickstart/swift.mdx— full rewrite converging on the JS/Java quickstart structure:channel.object.get(), plugin registration viaclientOptions.plugins = [.liveObjects: Plugin.self], channel modes, and the path-based create → subscribe → update flow. Install is SPM only (the CocoaPodsAblypod does not package the LiveObjects sources); platform minimums iOS 14 / macOS 11 / tvOS 14, Xcode 16.3+. Removes the manualattach()step (attach is implicit viaget()).Concepts
concepts/objects.mdx,operations.mdx— Channel object / PathObject / Instance sections opened to Swift; the old Root-object, aliasing/cyclic-reference, and stale-reference regions removed (not expressible in the path model);ObjectMessage/ObjectOperation/ObjectDatatables extended to Swift (ObjectOperationActionwith seven lowerCamelCase cases,ObjectsMapSemantics.lww,Datafor binary).concepts/path-object.mdx,instance.mdx— Swift added to every code block (25 and 19 fences respectively), plus Swift Type inference sections. The path layer keeps the never-throwing-casts story (wrong reads →nil, wrong writes → localARTErrorInfo92007 before anything is sent); the instance layer diverges deliberately:Instanceis a Swift enum, so discrimination is an exhaustiveswitch/if caseand Java's throwing-cast mismatch path cannot occur. The#typing,#read-valuesand#get-a-compact-objectanchors are preserved under the Swift toggle.Object types
map.mdx,counter.mdx— path-based Swift fences added beside the JS/Java ones in every section; the entire old-API Swift tails deleted (duplicate#create/#set/#remove/#size/#valueanchors collapse to one definition each; retired Swift-only anchors verified referrer-free).Feature pages
lifecycle.mdx— sync events via zero-argon(event:)returningStatusSubscription(per-tokenoff(), nooffAll()); deletion detection rewritten as an Instance subscription, source-verified:objectDeleteis delivered only through Instance subscriptions, which auto-unsubscribe after one final notification.typing.mdx— Swift Type inference section (#type-inference,#path-layer-casts,#instance-layer-casts,#get-type); TS content stays JS-gated.inband-objects.mdx— Swift fences for the[meta]objectsPub/Sub examples; fixed three link toggles that previously excluded Swift (including the plugin link that pointed at the JavaScript quickstart).batch.mdx,storage.mdx,index.mdx,synchronization.mdx— Swift toggles aligned with the Java treatment (channel objectwording, factual batch note).API reference & supporting config
api/realtime-sdk/channels.mdx— Swift joins the#### objectaccessor (extension property wording); the old Swift-only#### objectsentry removed.data/nav/liveobjects.ts— Swift SDK reference link repointed to the ably-cocoa-hosted DocC build (with aTODOto verify once published); label renamed "Swift plugin" → "Swift SDK".data/languages/languageData.ts— Swift LiveObjects version0.4→1.2(the plugin now ships inside ably-cocoa).Asynchronous API guidance
Swift's surface has no blocking variants and no
.join()analogue, so the JavaCompletableFutureguidance is replaced by a simpler story, stated once in the quickstart and echoed by a short aside on the mutation-heavy pages:get()suspend — call them withtry await. Reads, navigation and casts are synchronous and local.nil/empty); they throw only when the channel is detached/failed or theobject_subscribemode is missing.subscribe, the docs show thefor await event in try object.events()AsyncStreamform; terminating the stream unsubscribes automatically.Guarantees & validation
<If lang>gate widenings — verified against the diff: every removed line is old Swift-only content, an in-place Swift fence rewrite, or a gate-value change.AblyLiveObjectsproduct (throwaway SwiftPM harness on the ably-cocoa checkout, Swift 6.1).asPrimitive()cast,type()naming, async model).#root-objectandchannels#objectsanchors have zero remaining referrers repo-wide.Features not expressible in Swift (documented as such, not fabricated)
batch()compact()compactJson()onlyasString(),asNumber(), …)asPrimitive()returning thePrimitiveenum, pattern-matched by the callerInstanceis an enum — discrimination is compile-time exhaustiveoffAll()off()onlyFollow-ups
nav/liveobjects.tscarries aTODO— verify it resolves once the ably-cocoa-hostedAblyLiveObjectsdocumentation is published.Plugin.swiftdocstring still shows the removedchannel.objects.getRoot()example, andNotImplemented.swift's "skeleton" note predates the completed implementation.🤖 Generated with Claude Code