Skip to content

Conversation

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian commented Oct 3, 2025

Have gone through the public API to check it before locking it down for v1. Here are a bunch of miscellaneous changes this resulted in. There are still a few more to come.

Docs changes in ably/docs#2868.

Summary by CodeRabbit

  • New Features

    • RoomStatus gains isReleasing and isReleased helpers.
    • New initializers for reaction summaries and reaction events.
  • Refactor

    • Reaction identifiers normalized to clientID; related types renamed accordingly.
    • Reactions API now accepts messageSerial labels.
    • Messages and UI flows consistently use message serials for identification.
    • Occupancy.current is a non-throwing read-only property.
    • Several public types lost Equatable; chat API constructor visibility reduced.
  • Tests

    • Updated for API renames, mutability and @mainactor changes.

@coderabbitai
Copy link

coderabbitai bot commented Oct 3, 2025

Walkthrough

Switched UI and internal logic to use message.serial instead of message.id; renamed ClientId* → ClientID* and clientIds → clientIDs across reactions and JSON; reaction API parameter labels changed to messageSerial; occupancy current() became a property; removed multiple Equatable conformances and added @mainactor for paginated results; assorted initializer and visibility tweaks.

Changes

Cohort / File(s) Summary
Message identity & UI
Sources/AblyChat/Message.swift, Example/AblyChatExample/ContentView.swift, Tests/AblyChatTests/ChatAPITests.swift, Tests/AblyChatTests/IntegrationTests.swift
Message no longer Identifiable (id removed); UI and lookups switched to message.serial; Message.copy signature changed (reactions removed); Message.with(_:) updated; tests adjusted to compare messages by serial-aware logic; Version init now requires clientID.
Reactions API & types
Sources/AblyChat/MessageReactions.swift, Sources/AblyChat/DefaultMessageReactions.swift, Sources/AblyChat/MessageReaction.swift, Sources/AblyChat/MessageReaction+JSON.swift, Example/AblyChatExample/MessageViews/MessageReactionSummaryView.swift, Example/AblyChatExample/MessageViews/MessageReactionsSheet.swift, Example/AblyChatExample/Mocks/MockClients.swift, Tests/AblyChatTests/DefaultMessageReactionsTests.swift
Renamed ClientIdList/ClientIdCountsClientIDList/ClientIDCounts and clientIdsclientIDs; added/updated initializers and JSON keys (including clipped); changed reaction methods to use messageSerial: parameter label; updated all call sites and tests.
Typing clientID rename
Sources/AblyChat/Typing.swift, Sources/AblyChat/DefaultTyping.swift, Example/AblyChatExample/Mocks/MockClients.swift, Tests/AblyChatTests/DefaultTypingTests.swift
TypingSetEvent.Change parameter/property renamed clientIdclientID; emitters and tests updated.
Occupancy API & behavior
Sources/AblyChat/Occupancy.swift, Sources/AblyChat/DefaultOccupancy.swift, Example/AblyChatExample/Mocks/MockClients.swift, Tests/AblyChatTests/DefaultRoomOccupancyTests.swift, Sources/AblyChat/Errors.swift
current() throwing method → non-throwing current property; OccupancyEventType lost String raw type; OccupancyEvent fields became var; removed occupancy-related internal ChatError case; tests/mocks updated.
Equatable removals & test shims
Sources/AblyChat/DiscontinuityEvent.swift, Sources/AblyChat/Room.swift, Sources/AblyChat/RoomStatus.swift, Sources/AblyChat/Messages.swift, Sources/AblyChat/InternalError.swift, Sources/AblyChat/Errors.swift, Tests/AblyChatTests/Helpers/Equatable.swift
Removed Equatable from multiple public/internal types (DiscontinuityEvent, RoomStatusChange/RoomStatus, ChatMessageEvent, InternalError); added test-only Equatable helpers and identity-based comparisons to preserve tests; RoomStatus gained isReleasing/isReleased.
PaginatedResult @mainactor & mocks
Sources/AblyChat/PaginatedResult.swift, Tests/AblyChatTests/MessageSubscriptionAsyncSequenceTests.swift
Annotated PaginatedResult-related APIs with @MainActor and removed Equatable from the protocol; wrapper and conversion helpers marked @MainActor; test mocks updated to conform to @MainActor Equatable.
Room options internal equality
Sources/AblyChat/RoomOptions.swift, Sources/AblyChat/Rooms.swift
Dropped public Equatable from options structs; introduced internal EquatableBox types and equatableBox accessors used in Rooms.get(...) comparison.
Room lifecycle guard refactor
Sources/AblyChat/RoomLifecycleManager.swift
Replaced value-based ATTACHING-state check with guard case let .attached(error) = ... , error == nil pattern-matching and adjusted error sourcing.
Constructor & visibility changes
Sources/AblyChat/ChatAPI.swift
ChatAPI public initializer reduced to internal.
Subscription sendability & docs
Sources/AblyChat/SubscriptionAsyncSequence.swift
Class declaration updated to conform to Sendable, AsyncSequence (removed @unchecked); doc comment adjusted.
Example mocks cleanup
Example/AblyChatExample/Mocks/Misc.swift
Removed a dead custom equality operator on MockMessagesPaginatedResult that previously fatalErrored.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as App UI
  participant Reactions as MessageReactions
  participant API as ChatAPI
  participant Backend as Backend
  participant Store as Message Store

  User->>UI: tap reaction on message
  UI->>Reactions: send(messageSerial: serial, params)
  Reactions->>API: send(messageSerial: serial, params)
  API->>Backend: POST reaction for messageSerial
  Backend-->>API: ack + reaction event
  API-->>Store: emit MessageReactionSummaryEvent
  Store-->>UI: update message.reactions by serial
  UI-->>User: render updated reactions
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • umair-ably
  • maratal

Poem

I nibble serials, not the id,
clientIDs hop close with pride.
Reactions scatter, then align,
occupancy hums, actors shine.
Equality sleeps; tests still cheer — a rabbit's hop brings changes near. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The summary indicates the addition of unrelated Python code methods in src/calculator.py that do not pertain to the pre-v1 Swift API audit and conflict with the PR’s stated objectives. These changes are outside the scope of the linked issue and the intended public API adjustments. Please remove or relocate the unrelated Python calculator code changes into a separate PR so that this pull request remains focused on the Swift public API audit.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly communicates the main operation as a pre-v1 public API audit and references the linked issue, giving clear context to reviewers. It focuses on the core objective without extraneous details or vague terms.
Linked Issues Check ✅ Passed The pull request thoroughly implements the public API adjustments and refinements outlined in issue ECO-5577 by modifying visibility, parameter labels, type conformances, and initializers across the Swift codebase. All changes directly align with the objective of locking down the public API ahead of v1, and no required code updates from the linked audit are missing.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pre-v1-audit

📜 Recent 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 52fe739 and 9fc668d.

📒 Files selected for processing (1)
  • Sources/AblyChat/Occupancy.swift (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/AblyChat/Occupancy.swift
⏰ 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). (11)
  • GitHub Check: Example app, tvOS (Xcode 26.0)
  • GitHub Check: Example app, iOS (Xcode 26.0)
  • GitHub Check: Xcode, tvOS (Xcode 26.0)
  • GitHub Check: Example app, macOS (Xcode 26.0)
  • GitHub Check: Xcode, macOS (Xcode 26.0)
  • GitHub Check: Xcode, iOS (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 26.0)
  • GitHub Check: SPM (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 26.0)
  • GitHub Check: SPM, release configuration (Xcode 26.0)

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 base branch from main to 267-revert-typed-throws-crashes October 3, 2025 11:43
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 11:47 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 12:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 12:05 Inactive
@lawrence-forooghian lawrence-forooghian changed the base branch from 267-revert-typed-throws-crashes to 2025-10-03-enable-upcoming-language-features October 3, 2025 12:44
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 12:44 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 15:00 Inactive
@lawrence-forooghian lawrence-forooghian force-pushed the 2025-10-03-enable-upcoming-language-features branch from f59a5b1 to a43b818 Compare October 3, 2025 16:06
@lawrence-forooghian lawrence-forooghian changed the base branch from 2025-10-03-enable-upcoming-language-features to remove-existential-from-public-api October 3, 2025 16:09
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 16:10 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 3, 2025 18:28 Inactive
@lawrence-forooghian lawrence-forooghian changed the base branch from remove-existential-from-public-api to rename-DefaultChatClient-to-ChatClient October 3, 2025 19:09
Base automatically changed from rename-DefaultChatClient-to-ChatClient to main October 6, 2025 17:19
I want to be careful about the conformances that we commit to in our
public API now that we're locking it down for v1, especially when it
looks like they were just added for our testing purposes. We can revisit
in #10.
Not sure why I didn't do this in fc83fc1; probably I thought of it as a
data container more than a client, but really it's a client given its
methods for fetching further pages. (The compiler didn't require us to
make it @mainactor because our internal implementation is currently
going directly via the ably-cocoa paginated result type which isn't
marked as @mainactor, but we want to be sure that we will be able to
write a PaginatedResult implementation in any future client that _does_
enforce main actor isolation, e.g. upcoming ably-swift).
@lawrence-forooghian lawrence-forooghian marked this pull request as ready for review October 7, 2025 12:01
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Tests/AblyChatTests/ChatAPITests.swift (1)

150-162: Likely compile error: Version initializer missing clientID

Earlier in this file (Line 55) Version requires clientID. These initializations omit it.

-                    version: .init(serial: "3446456", timestamp: Date(timeIntervalSince1970: 1_730_943_049.269)), // from successGetMessagesWithItems
+                    version: .init(serial: "3446456", timestamp: Date(timeIntervalSince1970: 1_730_943_049.269), clientID: "random"), // from successGetMessagesWithItems
                     timestamp: Date(timeIntervalSince1970: 1_730_943_049.269),
                 ),
                 Message(
                     serial: "3446457",
                     action: .create,
                     clientID: "random",
                     text: "hello response",
                     metadata: [:],
                     headers: [:],
-                    version: .init(serial: "3446457", timestamp: Date(timeIntervalSince1970: 1_730_943_051.269)),
+                    version: .init(serial: "3446457", timestamp: Date(timeIntervalSince1970: 1_730_943_051.269), clientID: "random"),
                     timestamp: Date(timeIntervalSince1970: 1_730_943_051.269),
                 ),
Sources/AblyChat/Messages.swift (1)

322-333: Restore init(message:) to public
External code in Example/AblyChatExample/Mocks/MockClients.swift (lines 147, 175, 190, 209) and Tests/AblyChatTests/MessageSubscriptionAsyncSequenceTests.swift (lines 35, 44–45) calls this initializer and will fail with internal visibility. Change it back to public init(message:).

🧹 Nitpick comments (8)
Sources/AblyChat/SubscriptionAsyncSequence.swift (1)

97-112: Consider optimizing the termination handler pattern.

The current implementation replaces continuation.onTermination every time a handler is added. While correct, this has performance implications:

  1. Each call to addTerminationHandler rebuilds the entire closure that calls all accumulated handlers
  2. Handlers added after stream termination will never execute (AsyncStream limitation)

Consider setting onTermination once during initialization and having it iterate over the current state of terminationHandlers at termination time. However, this would require careful synchronization since terminationHandlers is @mainactor isolated while onTermination may be called from any context.

Example alternative approach:

// In init(bufferingPolicy:)
continuation.onTermination = { [weak self] _ in
    Task { @MainActor [weak self] in
        guard let self else { return }
        for handler in self.terminationHandlers {
            handler()
        }
    }
}
Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (1)

88-91: Consider isolating current to MainActor.

current is observed on the main actor. To avoid data races and match subscription callbacks, consider making Occupancy.current main-actor isolated, or document its thread-safety.

Tests/AblyChatTests/MessageSubscriptionAsyncSequenceTests.swift (1)

6-6: Main-actor–isolated Equatable conformance: verify toolchain support or fall back.

@MainActor on a specific protocol conformance is relatively new syntax. If your toolchain balks, prefer either:

  • Annotate the type: @MainActor private final class MockPaginatedResult..., or
  • Annotate the requirement:
-    static func == (lhs: MockPaginatedResult<Item>, rhs: MockPaginatedResult<Item>) -> Bool {
+    @MainActor
+    static func == (lhs: MockPaginatedResult<Item>, rhs: MockPaginatedResult<Item>) -> Bool {
Sources/AblyChat/RoomOptions.swift (1)

137-160: EquatableBox drift risk — add guardrails

Manual mapping is easy to forget when options evolve. Add a small test ensuring equatableBox replays all properties (or generate with Sourcery as noted). A simple approach: assert that EquatableBox init mirrors default-initialized options and property-by-property mutations affect equality.

Also applies to: 162-174, 176-190, 192-204, 206-214, 216-228

Example/AblyChatExample/MessageViews/MessageReactionsSheet.swift (1)

20-31: Nit: name the loop variable clientID for consistency

Aligns with new property clientIDs and broader ClientID naming.

-        for (emoji, clientList) in uniqueOrDistinct {
-            for clientId in clientList.clientIDs {
-                let key = "\(clientId)-\(emoji)"
+        for (emoji, clientList) in uniqueOrDistinct {
+            for clientID in clientList.clientIDs {
+                let key = "\(clientID)-\(emoji)"
                 reactions[key] = ReactionItem(
                     emoji: emoji,
-                    author: clientId,
+                    author: clientID,
                     count: 1,
                 )
             }
         }
Tests/AblyChatTests/IntegrationTests.swift (1)

287-291: Remove duplicate serial assertions

Serial equality is asserted twice in each block. Keep one to reduce noise.

-        #expect(rxEditedMessageFromSubscription.version.serial == txEditedMessage.version.serial)
-        #expect(rxEditedMessageFromSubscription.serial == txEditedMessage.serial)
+        #expect(rxEditedMessageFromSubscription.version.serial == txEditedMessage.version.serial)
@@
-        #expect(rxDeletedMessageFromSubscription.version.serial == txDeleteMessage.version.serial)
-        #expect(rxDeletedMessageFromSubscription.serial == txDeleteMessage.serial)
+        #expect(rxDeletedMessageFromSubscription.version.serial == txDeleteMessage.version.serial)

Also applies to: 311-315

Tests/AblyChatTests/Helpers/Equatable.swift (1)

5-47: Scope Equatable operators to test-only (internal) visibility

public here is unnecessary in the test target; default/internal is enough.

-extension RoomStatus: Equatable {
-    public static func == (lhs: RoomStatus, rhs: RoomStatus) -> Bool {
+extension RoomStatus: Equatable {
+    static func == (lhs: RoomStatus, rhs: RoomStatus) -> Bool {
@@
-extension RoomStatusChange: Equatable {
-    public static func == (lhs: RoomStatusChange, rhs: RoomStatusChange) -> Bool {
+extension RoomStatusChange: Equatable {
+    static func == (lhs: RoomStatusChange, rhs: RoomStatusChange) -> Bool {
@@
-extension DiscontinuityEvent: Equatable {
-    public static func == (lhs: DiscontinuityEvent, rhs: DiscontinuityEvent) -> Bool {
+extension DiscontinuityEvent: Equatable {
+    static func == (lhs: DiscontinuityEvent, rhs: DiscontinuityEvent) -> Bool {
@@
-extension RoomOptions: Equatable {
-    public static func == (lhs: RoomOptions, rhs: RoomOptions) -> Bool {
+extension RoomOptions: Equatable {
+    static func == (lhs: RoomOptions, rhs: RoomOptions) -> Bool {

Also applies to: 49-53, 55-59, 61-65

Sources/AblyChat/Messages.swift (1)

313-315: Review Equatable removal and mutability change

ChatMessageEvent no longer conforms to Equatable and its properties were changed from let to var, weakening the public API contract. Ensure no code or tests compare events with == and confirm the increased mutability is intentional.

📜 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 ea7cc29 and 9b070a0.

📒 Files selected for processing (34)
  • Example/AblyChatExample/ContentView.swift (6 hunks)
  • Example/AblyChatExample/MessageViews/MessageReactionSummaryView.swift (2 hunks)
  • Example/AblyChatExample/MessageViews/MessageReactionsSheet.swift (1 hunks)
  • Example/AblyChatExample/Mocks/Misc.swift (0 hunks)
  • Example/AblyChatExample/Mocks/MockClients.swift (7 hunks)
  • Sources/AblyChat/ChatAPI.swift (1 hunks)
  • Sources/AblyChat/DefaultMessageReactions.swift (2 hunks)
  • Sources/AblyChat/DefaultOccupancy.swift (2 hunks)
  • Sources/AblyChat/DefaultTyping.swift (3 hunks)
  • Sources/AblyChat/DiscontinuityEvent.swift (1 hunks)
  • Sources/AblyChat/Errors.swift (2 hunks)
  • Sources/AblyChat/InternalError.swift (0 hunks)
  • Sources/AblyChat/Message.swift (3 hunks)
  • Sources/AblyChat/MessageReaction+JSON.swift (6 hunks)
  • Sources/AblyChat/MessageReaction.swift (6 hunks)
  • Sources/AblyChat/MessageReactions.swift (2 hunks)
  • Sources/AblyChat/Messages.swift (1 hunks)
  • Sources/AblyChat/Occupancy.swift (2 hunks)
  • Sources/AblyChat/PaginatedResult.swift (4 hunks)
  • Sources/AblyChat/Room.swift (1 hunks)
  • Sources/AblyChat/RoomLifecycleManager.swift (1 hunks)
  • Sources/AblyChat/RoomOptions.swift (6 hunks)
  • Sources/AblyChat/RoomReactions.swift (1 hunks)
  • Sources/AblyChat/RoomStatus.swift (2 hunks)
  • Sources/AblyChat/Rooms.swift (1 hunks)
  • Sources/AblyChat/SubscriptionAsyncSequence.swift (1 hunks)
  • Sources/AblyChat/Typing.swift (1 hunks)
  • Tests/AblyChatTests/ChatAPITests.swift (3 hunks)
  • Tests/AblyChatTests/DefaultMessageReactionsTests.swift (6 hunks)
  • Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (3 hunks)
  • Tests/AblyChatTests/DefaultTypingTests.swift (3 hunks)
  • Tests/AblyChatTests/Helpers/Equatable.swift (1 hunks)
  • Tests/AblyChatTests/IntegrationTests.swift (9 hunks)
  • Tests/AblyChatTests/MessageSubscriptionAsyncSequenceTests.swift (2 hunks)
💤 Files with no reviewable changes (2)
  • Example/AblyChatExample/Mocks/Misc.swift
  • Sources/AblyChat/InternalError.swift
🧰 Additional context used
🧬 Code graph analysis (7)
Tests/AblyChatTests/IntegrationTests.swift (2)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Sources/AblyChat/MessageReactions.swift (2)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Example/AblyChatExample/ContentView.swift (2)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Sources/AblyChat/MessageReaction+JSON.swift (1)
Sources/AblyChat/JSONCodable.swift (4)
  • arrayValueForKey (94-104)
  • optionalBoolValueForKey (219-233)
  • objectValueForKey (58-68)
  • optionalNumberValueForKey (181-195)
Example/AblyChatExample/Mocks/MockClients.swift (1)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (3)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Sources/AblyChat/DefaultMessages.swift (2)
  • send (126-132)
  • delete (142-148)
Sources/AblyChat/DefaultMessageReactions.swift (1)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
🔇 Additional comments (31)
Sources/AblyChat/SubscriptionAsyncSequence.swift (1)

13-14: LGTM! Removing @unchecked improves type safety.

The removal of @unchecked from the Sendable conformance is appropriate and safe:

  • terminationHandlers (line 54) is @mainactor isolated, ensuring safe concurrent access
  • mode (line 55) is immutable, preventing data races
  • All associated types (Mode, AsyncStream, AnyNonThrowingAsyncSequence) are properly marked as Sendable

The documentation update warning about single iteration is also helpful and aligns with the expected behavior of AsyncStream-based sequences.

Sources/AblyChat/RoomReactions.swift (1)

111-113: Raw‐type removal on RoomReactionEventType is safe
RoomReactionEventType isn’t referenced by any .rawValue or init(rawValue:)—all existing raw‐value lookups use the internal RoomReactionEvents enum. No action required.

Sources/AblyChat/Room.swift (1)

201-201: Equatable removed from RoomStatusChange – confirm migration notes and usages.

Pre‑v1 break makes sense; please ensure public docs/samples/tests are updated to avoid relying on Equatable and show comparing current/previous instead.

Sources/AblyChat/RoomStatus.swift (2)

6-6: RoomStatus no longer Equatable – check downstream constraints.

Looks consistent with the API audit. Please verify no public generics/collections still require RoomStatus: Equatable (docs/samples too).


126-140: New helpers isReleasing/isReleased: LGTM.

Consistent with existing case-check helpers.

Sources/AblyChat/Rooms.swift (1)

176-180: Switch to equatableBox for RoomOptions equality: good alignment with internalized equality.

Please confirm equatableBox includes all nested option fields (messages/presence/occupancy/typing) and that tests cover equivalence/non‑equivalence cases.

Sources/AblyChat/DiscontinuityEvent.swift (1)

3-3: Equatable removed from DiscontinuityEvent – verify doc/sample updates.

Ensure any public examples/tests no longer rely on Equatable and demonstrate comparing error properties instead.

Sources/AblyChat/ChatAPI.swift (1)

14-16: Make ChatAPI init internal: LGTM.

Matches class visibility and pre‑v1 surface reduction. Please confirm no public docs/samples still instantiate ChatAPI directly.

Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (2)

39-41: Confirm contract: get() does not mutate current.

This test assumes current stays nil after get(). If implementation later caches results into current, this will become brittle. Please confirm or document the contract.


125-126: Comment adjustment LGTM.

Not testable due to fatalError is fine; aligns with the chosen failure semantics.

Tests/AblyChatTests/MessageSubscriptionAsyncSequenceTests.swift (1)

50-57: Method isolation LGTM.

Annotating the test with @MainActor matches pagination’s main-actor isolation.

Example/AblyChatExample/ContentView.swift (1)

63-63: Approve change; no remaining message.id references
Migration to message.serial is consistent across UI flows; grep confirms no occurrences of message.id. Consider adding a helper like updateMessage(serial:_:) to DRY the “find index by serial then replace item” pattern.

Tests/AblyChatTests/DefaultTypingTests.swift (1)

98-99: No leftover old clientId labels or properties All .init(clientID:) and change.clientID occurrences are updated; no change.clientId or init(clientId:) patterns remain.

Sources/AblyChat/RoomOptions.swift (1)

6-39: Public Equatable removal + Sendable usage looks good

API surface is cleaner; internal equality handled elsewhere. No functional concerns.

Also applies to: 44-57, 62-87, 92-108, 113-115, 120-133

Sources/AblyChat/DefaultMessageReactions.swift (1)

22-22: LGTM on signature alignment

Internal methods now match protocol label changes; bodies unchanged and correct.

Also applies to: 43-43

Tests/AblyChatTests/IntegrationTests.swift (1)

121-136: Helper comparator for Messages is appropriate

Good approach to ignore non-serial version fields in equality checks.

Tests/AblyChatTests/DefaultMessageReactionsTests.swift (1)

27-29: LGTM on reaction API and ClientID renames

Tests align with messageSerial labels and ClientIDList/clientIDs. Request/params expectations remain correct.

Also applies to: 71-74, 96-97, 149-164, 349-350, 388-389

Sources/AblyChat/MessageReactions.swift (1)

21-21: API label rename complete; no send(to:)/delete(from:) usages found Approving.

Sources/AblyChat/MessageReaction+JSON.swift (2)

69-81: Approve the naming update.

Renaming ClientIdList to ClientIDList and updating clientIds to clientIDs follows Swift naming conventions where acronyms are capitalized consistently. The JSONKey still correctly maps clientIds from the backend format.


83-99: Approve the naming update.

Renaming ClientIdCounts to ClientIDCounts and updating clientIds/totalClientIds to clientIDs/totalClientIDs maintains consistency with the updated naming conventions. The JSONKey correctly maps backend keys (clientIds, totalClientIds) to the new property names.

Sources/AblyChat/PaginatedResult.swift (4)

3-15: Approve the concurrency annotation.

Adding @MainActor to the PaginatedResult protocol ensures all pagination operations run on the main thread, which is appropriate for UI-driven pagination. Removing Equatable from the protocol while keeping it on the concrete implementation (PaginatedResultWrapper) reduces the protocol's requirements and allows non-equatable implementations if needed.


21-45: Concurrency context correctly propagated.

The @MainActor annotation on handleResponse ensures the continuation is resumed on the main actor, aligning with the protocol's requirements.


53-107: Equatable conformance correctly scoped.

The @MainActor Equatable conformance on PaginatedResultWrapper correctly isolates equatable comparison to the main actor. This is necessary since the type is now main-actor-isolated.


111-114: Factory method correctly annotated.

The @MainActor annotation on toPaginatedResult ensures the factory method executes in the main actor context, maintaining consistency with the protocol requirements.

Sources/AblyChat/Message.swift (3)

104-124: Approve the copy() refactor.

The new implementation is more efficient—it mutates a local copy of self rather than constructing a new Message with all fields. This is cleaner and reduces the risk of accidentally omitting fields when the struct grows.


218-227: Approve the with(summaryEvent:) implementation.

The method correctly validates that the event's messageSerial matches the message's serial before applying the summary. The error code 40000 with status 400 is appropriate for a client-side validation error.


22-26: Removed Identifiable conformance. No ForEach/List over Message found in Example/ or Tests/; verify across all SwiftUI views that any ForEach/List iterating Message collections now includes id: \.serial.

Sources/AblyChat/Occupancy.swift (3)

36-40: Confirm runtime guard for current property

The current property includes a guard options.enableEvents check that triggers fatalError when occupancy events are disabled.


103-104: Confirm mutability of OccupancyEvent properties
No assignments found mutating type or occupancy post-initialization. Ensure changing them from let to var aligns with intended use cases; otherwise revert to immutable.


97-99: No references to OccupancyEventType.rawValue found; removal of String conformance is safe.

Sources/AblyChat/Messages.swift (1)

306-310: String conformance removal from ChatMessageEventType is safe. No .rawValue usages found in the codebase.

@github-actions github-actions bot temporarily deployed to staging/pull/358/AblyChat October 7, 2025 12:17 Inactive
There's no reason for a user to want to compare these, and I don't think
that ARTErrorInfo has a useful -isEqual: implementation.
I believe that this was added for the example app (which no longer needs
it), but I don't think it was the right approach; Identifiable is a
context-specific concept and I don't think there's a one-size-fits-all
conformance for Message.
There's no obvious reason for users to want to compare these and I think
we should maintain the flexibility to add non-Equatable values (e.g.
references to some other object) here.
There's no reason for a user to wish to compare two of these, and we
want the flexibility to be able to add non-Equatable properties (e.g. a
callback) in the future.
We got rid of them in 8f6626a, but these ones snuck in in ac39e82.
Not sure what the logic was for the `to:`, but it's not clear at the
call site what needs to be passed for this value.
Not sure why we added this in d7e9adf; it's not part of the JS public
API.
For consistency with how we handle this scenario for other features.
For consistency with the JS API and because it doesn't communicate very
much (the method is mysterious either way).
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: 2

♻️ Duplicate comments (1)
Sources/AblyChat/RoomReactions.swift (1)

117-118: Event properties should remain immutable.

As noted in previous review comments, changing these properties from let to var makes events mutable, which contradicts the typical semantics of events as immutable snapshots. Consider reverting to let unless there's a specific requirement for post-creation mutation.

📜 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 9b070a0 and 52fe739.

📒 Files selected for processing (21)
  • Example/AblyChatExample/ContentView.swift (6 hunks)
  • Example/AblyChatExample/Mocks/MockClients.swift (7 hunks)
  • Sources/AblyChat/DefaultMessageReactions.swift (2 hunks)
  • Sources/AblyChat/DefaultOccupancy.swift (2 hunks)
  • Sources/AblyChat/DiscontinuityEvent.swift (1 hunks)
  • Sources/AblyChat/Errors.swift (2 hunks)
  • Sources/AblyChat/InternalError.swift (0 hunks)
  • Sources/AblyChat/Message.swift (3 hunks)
  • Sources/AblyChat/MessageReactions.swift (2 hunks)
  • Sources/AblyChat/Messages.swift (1 hunks)
  • Sources/AblyChat/Occupancy.swift (2 hunks)
  • Sources/AblyChat/Room.swift (1 hunks)
  • Sources/AblyChat/RoomLifecycleManager.swift (1 hunks)
  • Sources/AblyChat/RoomOptions.swift (6 hunks)
  • Sources/AblyChat/RoomReactions.swift (1 hunks)
  • Sources/AblyChat/RoomStatus.swift (2 hunks)
  • Sources/AblyChat/Rooms.swift (1 hunks)
  • Tests/AblyChatTests/DefaultMessageReactionsTests.swift (6 hunks)
  • Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (3 hunks)
  • Tests/AblyChatTests/Helpers/Equatable.swift (1 hunks)
  • Tests/AblyChatTests/IntegrationTests.swift (9 hunks)
💤 Files with no reviewable changes (1)
  • Sources/AblyChat/InternalError.swift
🚧 Files skipped from review as they are similar to previous changes (5)
  • Tests/AblyChatTests/IntegrationTests.swift
  • Sources/AblyChat/DefaultMessageReactions.swift
  • Sources/AblyChat/Rooms.swift
  • Tests/AblyChatTests/Helpers/Equatable.swift
  • Sources/AblyChat/Messages.swift
🧰 Additional context used
🧬 Code graph analysis (4)
Example/AblyChatExample/ContentView.swift (3)
Sources/AblyChat/Message.swift (1)
  • with (218-227)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Example/AblyChatExample/Mocks/MockClients.swift (1)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Sources/AblyChat/MessageReactions.swift (2)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (2)
Example/AblyChatExample/Mocks/MockClients.swift (5)
  • send (161-177)
  • send (249-266)
  • send (322-333)
  • delete (194-211)
  • delete (268-278)
Sources/AblyChat/DefaultMessageReactions.swift (2)
  • send (22-40)
  • delete (43-59)
⏰ 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). (11)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 26.0)
  • GitHub Check: Xcode, tvOS (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 26.0)
  • GitHub Check: Example app, macOS (Xcode 26.0)
  • GitHub Check: Example app, iOS (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 26.0)
  • GitHub Check: Example app, tvOS (Xcode 26.0)
  • GitHub Check: Xcode, macOS (Xcode 26.0)
  • GitHub Check: SPM (Xcode 26.0)
  • GitHub Check: Xcode, iOS (Xcode 26.0)
  • GitHub Check: SPM, release configuration (Xcode 26.0)
🔇 Additional comments (9)
Sources/AblyChat/DiscontinuityEvent.swift (1)

3-3: LGTM! Intentional breaking change as part of v1 API audit.

Removing Equatable conformance from the public API is appropriate for the v1 lockdown. The test suite maintains equality checking via separate test helpers, ensuring test coverage remains intact.

Sources/AblyChat/RoomReactions.swift (1)

111-111: No rawValue usage found; verify serialization behavior.
No code references to rawValue, init(rawValue:), or Codable were detected—ensure JSON encoding/decoding and any messaging serialization still produce the intended string for RoomReactionEventType.

Sources/AblyChat/RoomStatus.swift (2)

126-140: LGTM! Consistent API additions.

The new isReleasing and isReleased computed properties follow the established pattern of the existing helper properties and provide convenient boolean checks for these states.


6-6: Document removal of Equatable conformance from RoomStatus. Public RoomStatus no longer supports == (tests use a private helper extension to restore it). Ensure this breaking change is called out in release notes and the migration guide.

Sources/AblyChat/RoomLifecycleManager.swift (1)

613-616: LGTM! TOCTOU issue correctly addressed.

The guard statement now correctly inspects the captured nextRoomStatusChange.current status (rather than the potentially-advanced roomStatus), ensuring the condition and error cause refer to the same captured state. This resolves the race condition flagged in the previous review.

Sources/AblyChat/Room.swift (1)

201-201: Document removal of Equatable conformance from RoomStatusChange.
No == usages found; tests include a test-only extension—this remains a breaking change, so update release notes and migration guide accordingly.

Sources/AblyChat/Message.swift (1)

218-227: Serial guard matches spec.

Nice touch adding the serial check before applying the summary—this lines up with CHA-M11e and keeps reaction updates from drifting onto the wrong message.

Sources/AblyChat/Occupancy.swift (2)

40-40: LGTM: Property-based API is more idiomatic.

The change from a throwing method to a non-throwing property is appropriate for accessing cached state, and using a fatalError for programmer errors (accessing current when events are not enabled) follows Swift conventions.


97-99: Removing String raw‐value conformance is safe
Search found no rawValue, Codable conformance, or JSONEncoder/Decoder usage for OccupancyEventType; its removal has no impact.

Copy link
Collaborator

@maratal maratal left a comment

Choose a reason for hiding this comment

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

A few questions.

}

func send(to messageSerial: String, params: SendMessageReactionParams) async throws(ARTErrorInfo) {
func send(messageSerial: String, params: SendMessageReactionParams) async throws(ARTErrorInfo) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

But it's not "sending message serial", it's sending a reaction to the message with message serial. Ditto for delete.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think that the existing signature makes it clear what the argument means, though. It's just a String without any additional information.

I agree that you're not "sending a message serial", the same way that in:

  • Messages.send(params: SendMessageParams) you're sending a message, not "sending a params"
  • Presence.enter(data: PresenceData) you're not "entering a data"

etc.

And I'm not 100% sure what's the right signature for these things in Swift. Like, would the following be better, or just more verbose? I don't know.

  • MessageReactions.send(forMessageWithSerial:)
  • Messages.send(withParams:)
  • Presence.enter(withData:)

etc

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The key part of the API design guidelines though is "Prefer method and function names that make use sites form grammatical English phrases."

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So maybe the latter are better?

Copy link
Collaborator

@maratal maratal Oct 7, 2025

Choose a reason for hiding this comment

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

I think so, more descriptive and reflects things correctly. Feel free to resolve this once you decide the names.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK — can I do that one in a separate PR just so I don't have to deal with merge conflicts on #378?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Gonna merge this

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also consider this: MessageReactions.send(for messageWithSerial:) @lawrence-forooghian but I'm not sure, leaving you to decide.

@lawrence-forooghian lawrence-forooghian merged commit fe62f3b into main Oct 7, 2025
17 checks passed
@lawrence-forooghian lawrence-forooghian deleted the pre-v1-audit branch October 7, 2025 14:28
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