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).
| 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).
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).
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