Skip to content

Conversation

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian commented Sep 25, 2025

Note: This PR is based on top of #88; please review that one first.

This reverts the public API change of fd1e91c whilst preserving the internal MessagePack-related motivation for that commit.

Summary by CodeRabbit

  • Refactor
    • Updated numeric representation to Double across public APIs for more consistent number handling.
    • ExtendedJSONValue now distinguishes number and extra types, with updated serialization/deserialization and mapping APIs requiring number-specific converters.
    • API changes are breaking; updates may be required in code using number values.
  • Tests
    • Adjusted integration tests to use JavaScript Number.MAX_SAFE_INTEGER for cross-platform consistency.
    • Updated expectations to compare numeric values directly rather than via intermediate wrappers.

@coderabbitai
Copy link

coderabbitai bot commented Sep 25, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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.

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

Walkthrough

Reworks numeric handling by introducing a Number generic to ExtendedJSONValue and switching JSONValue numbers to Double. Updates serialization, deserialization, and mapping APIs to accept number transformers. WireValue bridges numbers via NSNumber. Tests adjust to Double-based numbers and JavaScript’s MAX_SAFE_INTEGER boundaries.

Changes

Cohort / File(s) Summary
ExtendedJSONValue: numeric generic and API updates
Sources/AblyLiveObjects/Utility/ExtendedJSONValue.swift
Adds second generic parameter for numbers: ExtendedJSONValue<Number, Extra>. Changes .number(NSNumber).number(Number). Extends init/serialize to accept create/serializeNumberValue. Updates recursive construction/serialization. Revises map to transform number and extra separately. Removes Extra == Never convenience.
JSONValue: Double-based numbers and bridging
Sources/AblyLiveObjects/Utility/JSONValue.swift
Changes .number payload to Double (from NSNumber). Updates numberValue type and literal conformances. Bridges via ExtendedJSONValue<Double, Never> with number creation/serialization closures. Adjusts JSONSerialization conversion paths accordingly.
WireValue: NSNumber bridging for transport
Sources/AblyLiveObjects/Utility/WireValue.swift
Uses NSNumber for wire-level numbers. Updates conversions with ExtendedJSONValue<NSNumber, ExtraValue>, mapping between Double (JSONValue) and NSNumber (WireValue). Applies serializeNumberValue and doubleValue where needed.
JS Integration test helpers
Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsHelper.swift
Writes numeric fields using native Swift numbers (no explicit NSNumber wrapping).
JS Integration tests
Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift
Introduces maxSafeInteger (Double) for JS compatibility. Replaces Int.max usages. Updates assertions to use numberValue instead of .doubleValue.
Object creation helpers tests
Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift
Expects Double-wrapped numeric in deserialized structures instead of NSNumber.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant JS as JSONSerialization
  participant JV as JSONValue (Double)
  participant EJV as ExtendedJSONValue<Number, Extra>
  participant WV as WireValue (NSNumber)

  Note over EJV: New: Number generic parameter

  JS->>EJV: init(deserialized:any, createNumberValue, createExtraValue)
  EJV-->>JV: map(number: to Double, extra: drop) -> ExtendedJSONValue<Double, Never>
  JV-->>JS: toJSONSerializationInputElement (serializeNumberValue: Double→NSNumber)

  rect rgba(220,240,255,0.4)
  JV->>EJV: toExtendedJSONValue()
  EJV-->>WV: map(number: Double→NSNumber, extra: passthrough)
  WV-->>EJV: toExtendedJSONValue() (NSNumber retained)
  EJV-->>JV: map(number: NSNumber.doubleValue, extra: drop)
  end

  Note over JV,WV: Numbers: JV=Double, WV=NSNumber, EJV=generic Number
Loading
sequenceDiagram
  participant In as Input Any
  participant E as ExtendedJSONValue<N, X>
  participant Out as Serialized Any

  In->>E: init(deserialized:, createNumberValue, createExtraValue)
  E->>Out: serialized(serializeNumberValue, serializeExtraValue)
  Note over E: .number uses provided closures throughout recursion
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

I nibble bytes and count to two,
Now Number stands beside Extra, too.
Doubles dance where NS once lay,
Wire rides Number the NSNumber way.
With MAX_SAFE bounds I hop and cheer—
Our JSON garden’s crystal-clear.
Thump-thump! The carrots engineer.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the main API change in this pull request, namely switching JSONValue's number case back to a Double. It directly maps to the PR’s objective of reverting the number case from NSNumber to Double. The phrasing is clear, concise, and specific enough for a reviewer to understand the primary change at a glance.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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 and usage tips.

@lawrence-forooghian lawrence-forooghian changed the title Switch JSONValue's number case back to Double Switch JSONValue's number case back to Double Sep 25, 2025
@lawrence-forooghian lawrence-forooghian changed the title Switch JSONValue's number case back to Double Switch JSONValue's number case back to Double Sep 25, 2025
@lawrence-forooghian
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Sep 25, 2025

✅ Actions performed

Review triggered.

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/91/AblyLiveObjects September 25, 2025 16:48 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
Sources/AblyLiveObjects/Utility/WireValue.swift (1)

238-249: Precision loss when converting NSNumber → Double (int > 2^53).

This is expected after reverting JSONValue.number to Double, but it’s worth guarding in debug builds to surface accidental oversized integers.

Apply an optional debug assertion to flag values beyond JS safe integer range:

-            let neverExtended = try toExtendedJSONValue.map(number: { (number: NSNumber) throws(ARTErrorInfo) -> Double in
-                number.doubleValue
+            let neverExtended = try toExtendedJSONValue.map(number: { (number: NSNumber) throws(ARTErrorInfo) -> Double in
+                let d = number.doubleValue
+                #if DEBUG
+                let maxSafeInteger = 9_007_199_254_740_991.0 // 2^53 - 1
+                if abs(d) > maxSafeInteger {
+                    assertionFailure("Precision loss: converting NSNumber (\(number)) to Double beyond JS safe integer range")
+                }
+                #endif
+                return d
             }, extra: { (extra: ExtraValue) throws(ARTErrorInfo) -> Never in

Also, please confirm that NSNumber’s Sendable conformance on your minimum deployment targets/toolchain is sufficient for WireValue: Sendable. If not, we may need to revisit Sendable here or wrap NSNumber accesses.

Sources/AblyLiveObjects/Utility/ExtendedJSONValue.swift (1)

70-93: map(number:extra:) redesign is clear and ergonomic.

Typed throws and separate transformers for number/extra are well done.

Consider adding lightweight typealiases for common instantiations to improve readability at call sites:

  • typealias JSONExtended = ExtendedJSONValue<Double, Never>
  • typealias WireExtended = ExtendedJSONValue<NSNumber, WireValue.ExtraValue>
Sources/AblyLiveObjects/Utility/JSONValue.swift (2)

113-115: Integer literal to Double: clarify precision limits.

Large integers (> 2^53-1) won’t round‑trip exactly as Double. Consider documenting this on the enum or the integer-literal init.


154-155: Prefer explicit NSNumber construction for serialization (and optionally guard non‑finite).

Use NSNumber(value:) to avoid brittle bridging and make intent explicit. Optionally, reject non‑finite values early.

Apply this diff:

-        toExtendedJSONValue.serialized(serializeNumberValue: { $0 as NSNumber }, serializeExtraValue: { _ in })
+        toExtendedJSONValue.serialized(
+            serializeNumberValue: { NSNumber(value: $0) },
+            serializeExtraValue: { _ in }
+        )

Optional stricter version:

-        toExtendedJSONValue.serialized(serializeNumberValue: { $0 as NSNumber }, serializeExtraValue: { _ in })
+        toExtendedJSONValue.serialized(
+            serializeNumberValue: {
+                precondition($0.isFinite, "Non-finite JSON number is not permitted")
+                return NSNumber(value: $0)
+            },
+            serializeExtraValue: { _ in }
+        )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 827652c and c891a47.

📒 Files selected for processing (6)
  • Sources/AblyLiveObjects/Utility/ExtendedJSONValue.swift (5 hunks)
  • Sources/AblyLiveObjects/Utility/JSONValue.swift (7 hunks)
  • Sources/AblyLiveObjects/Utility/WireValue.swift (7 hunks)
  • Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsHelper.swift (2 hunks)
  • Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift (6 hunks)
  • Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Example app, tvOS (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16.3)
  • GitHub Check: Example app, macOS (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16.3)
  • GitHub Check: Xcode, macOS (Xcode 16.3)
  • GitHub Check: Example app, iOS (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16.3)
  • GitHub Check: Xcode, tvOS (Xcode 16.3)
  • GitHub Check: Xcode, iOS (Xcode 16.3)
  • GitHub Check: SPM (Xcode 16.3)
  • GitHub Check: lint
  • GitHub Check: Generate code coverage
🔇 Additional comments (19)
Sources/AblyLiveObjects/Utility/WireValue.swift (5)

6-14: Good rationale for using NSNumber on the wire.

Using NSNumber here to preserve integer semantics for MessagePack encoding is the right call, while keeping JSONValue as Double.


125-135: Bridging from plugin support data looks correct.

Using ExtendedJSONValue<NSNumber, ExtraValue> with createNumberValue: { $0 } and explicit Data handling is clean and consistent.


152-158: Serialization path is consistent with the new ExtendedJSONValue API.

serializeNumberValue passes through NSNumber as intended, and extra values handle Data only.


177-197: Clean bidirectional mapping with ExtendedJSONValue<NSNumber, ExtraValue>.

The init and toExtendedJSONValue cases are exhaustive and correct.

Also applies to: 199-216


224-227: JSONValue → WireValue bridging is correct.

Mapping Double to NSNumber via map(number:) aligns with the new generics.

Sources/AblyLiveObjects/Utility/ExtendedJSONValue.swift (3)

3-12: Solid abstraction: parameterizing Number separately from Extra.

This cleanly enables Double for JSON and NSNumber for wire without duplicating logic.


20-42: Correct NSNumber/Bool discrimination during deserialization.

The kCFBooleanTrue/False identity check avoids the classic NSNumber(0/1) vs Bool pitfall.


47-64: Serialization hooks for numbers/extras are appropriate.

Propagating serializeNumberValue through nested structures keeps callers in control (NSNumber vs Double).

Please confirm that upstream JSON serialization paths reject or sanitize NaN/±Infinity (e.g., for JSONValue/Double). If not, consider guarding before invoking JSONSerialization to avoid runtime failures or invalid JSON.

Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsHelper.swift (1)

454-455: LGTM: JSON REST ops now use Double-based JSONValue.

Switching to .number(Double) for REST request bodies matches the public API revert.

Double-check all callers of counterCreateRestOp/counterIncRestOp were updated to pass Double (not NSNumber/Int) to avoid implicit conversions.

Also applies to: 470-471

Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift (1)

68-69: LGTM: Test expectation updated to Double.

Expecting semantics as Double aligns with JSONValue.number(Double).

Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift (3)

134-136: Good addition: Number.MAX_SAFE_INTEGER analogue.

Using 2^53−1 avoids accidental precision surprises in Double-backed numbers.


161-169: LGTM: Fixtures use maxSafeInteger and its negative.

Works well with Double-backed JSON values.


860-861: LGTM: Compare numberValue directly.

Switching from doubleValue to numberValue matches the JSONValue API change and keeps assertions straightforward.

Also applies to: 1076-1077, 2053-2054, 2353-2354

Sources/AblyLiveObjects/Utility/JSONValue.swift (6)

32-32: Revert to Double for numbers looks good; ensure semver/migration note.

Public API is restored to Double. Please add a short migration note (calls that used NSNumber now expect Double) and ensure the next release versioning reflects this source change.


66-72: numberValue -> Double? LGTM.

Type and accessor semantics are consistent with the enum case.


119-121: Float literal mapping is correct.

No concerns.


139-145: Deserialization bridge via doubleValue is appropriate.

Cleanly captures NSNumber to Double without changing control flow.


228-243: ExtendedJSONValue bridging initializer reads cleanly.

Cases are mapped 1:1; no hidden behavior changes.


245-260: Conversion back to ExtendedJSONValue is correct.

Number path now returns Double; object/array recursion intact.

@lawrence-forooghian lawrence-forooghian force-pushed the 39-less-passing-LiveMap-delegate branch from 827652c to 96799cb Compare September 29, 2025 12:19
This reverts the public API change of fd1e91c whilst preserving the
internal MessagePack-related motivation for that commit.
@lawrence-forooghian lawrence-forooghian force-pushed the switch-JSONValue-number-back-to-Double branch from c891a47 to 355497e Compare September 29, 2025 12:21
Base automatically changed from 39-less-passing-LiveMap-delegate to main September 29, 2025 12:42
@lawrence-forooghian lawrence-forooghian merged commit 9a12f17 into main Oct 2, 2025
18 checks passed
@lawrence-forooghian lawrence-forooghian deleted the switch-JSONValue-number-back-to-Double branch October 2, 2025 12:26
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.

4 participants