Problem statement
JSONValue is the type LiveObjects users meet first: it's what compactJson() returns, and printing it is the very first thing a quickstart reader does after fetching an object's state. But JSONValue declares no CustomStringConvertible conformance, so print(json) and string interpolation fall back to Swift's reflection-based rendering:
let json = try reactionsMap.compactJson() // logically {"likes": 10, "hearts": 0}
print("Reactions updated: \(json)")
prints:
Reactions updated: object(["hearts": AblyLiveObjects.JSONValue.number(0.0), "likes": AblyLiveObjects.JSONValue.number(10.0)])
This has three problems:
- It's noise. The type-name prefixes and enum-case wrappers bury the actual data. Nobody reading a console wants
AblyLiveObjects.JSONValue.number(10.0) where 10 would do.
- It's nondeterministic. Dictionary iteration order makes the printed key order vary run to run, so the same state prints differently each time.
- It's inconsistent with the other Ably SDKs. In ably-java,
compactJson() hands back a gson JsonObject whose toString() prints compact JSON text; in ably-js, console.log shows the plain object. Swift is the only SDK where printing the compact JSON doesn't give you something that looks like JSON — and the only one where the documentation's // console: example outputs can't literally match what the user sees.
There's also no public escape hatch: the JSONValue → Foundation bridge (toJSONSerializationInput) is internal, so a user who wants readable output has to hand-write a recursive renderer over the enum.
Proposed solution
Conform JSONValue to CustomStringConvertible, rendering compact JSON text, with object keys sorted for deterministic output:
extension JSONValue: CustomStringConvertible {
/// Compact JSON text, with object keys sorted for deterministic output.
public var description: String { ... }
}
With that in place, the example above prints:
Reactions updated: {"hearts":0,"likes":10}
Notes on the shape of the change:
- Additive and non-breaking: a conformance on a type this SDK owns, no signature changes.
- The implementation can reuse the existing internal serialization machinery, or a small recursive renderer over the six cases; sorted keys keep the output stable.
- Optionally also conform
CustomDebugStringConvertible so LLDB's po gives the same readable form.
Why this helps beyond nicer printing
- Debugging:
po json in LLDB and print in the console immediately show the state you actually care about, instead of enum plumbing. That matters most in exactly the situations where LiveObjects users reach for compactJson() — inspecting what the synchronized state currently is.
- Logging: interpolating a
JSONValue into any log line produces compact, greppable JSON.
- Testing: deterministic output (sorted keys) means test assertions and snapshot comparisons against printed state are reproducible.
- Documentation accuracy: the quickstart and reference examples show outputs like
// console: {"likes":10.0,"hearts":0.0}. Today those are a logical representation rather than what Swift actually prints; with this conformance they become literally true, matching the behavior ably-java and ably-js users already get.
References
┆Issue is synchronized with this Jira Task by Unito
Problem statement
JSONValueis the type LiveObjects users meet first: it's whatcompactJson()returns, and printing it is the very first thing a quickstart reader does after fetching an object's state. ButJSONValuedeclares noCustomStringConvertibleconformance, soprint(json)and string interpolation fall back to Swift's reflection-based rendering:prints:
This has three problems:
AblyLiveObjects.JSONValue.number(10.0)where10would do.compactJson()hands back a gsonJsonObjectwhosetoString()prints compact JSON text; in ably-js,console.logshows the plain object. Swift is the only SDK where printing the compact JSON doesn't give you something that looks like JSON — and the only one where the documentation's// console:example outputs can't literally match what the user sees.There's also no public escape hatch: the
JSONValue→ Foundation bridge (toJSONSerializationInput) isinternal, so a user who wants readable output has to hand-write a recursive renderer over the enum.Proposed solution
Conform
JSONValuetoCustomStringConvertible, rendering compact JSON text, with object keys sorted for deterministic output:With that in place, the example above prints:
Notes on the shape of the change:
CustomDebugStringConvertibleso LLDB'spogives the same readable form.Why this helps beyond nicer printing
po jsonin LLDB andprintin the console immediately show the state you actually care about, instead of enum plumbing. That matters most in exactly the situations where LiveObjects users reach forcompactJson()— inspecting what the synchronized state currently is.JSONValueinto any log line produces compact, greppable JSON.// console: {"likes":10.0,"hearts":0.0}. Today those are a logical representation rather than what Swift actually prints; with this conformance they become literally true, matching the behavior ably-java and ably-js users already get.References
as*convenience accessors to theInstanceenum #2242┆Issue is synchronized with this Jira Task by Unito