Conversation
WalkthroughReworks internal error handling by routing Ably/ART errors through Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant SDK as AblyChat SDK
participant Internal as InternalError
participant ART as ARTErrorInfo
Client->>SDK: perform operation (realtime/room/channel/message)
SDK->>SDK: run checks / call Ably Cocoa
alt Ably/ART error
SDK->>Internal: InternalError.fromAblyCocoa(ARTErrorInfo) %%#{"color":"#F6C352"}
Internal->>ART: .toARTErrorInfo() when reporting externally
SDK-->>Client: propagate failure (ARTErrorInfo / InternalError)
else Internal condition
SDK->>Internal: InternalError.internallyThrown(case) %%#{"color":"#9FD3C7"}
Internal->>ART: .toARTErrorInfo()
SDK-->>Client: propagate failure (ARTErrorInfo / InternalError)
else success
SDK-->>Client: return success
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
3dda7d8 to
7635627
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
Sources/AblyChat/DefaultMessages.swift (1)
182-183: Prefer logging the error’s messageUse InternalError.message for clearer logs instead of "(error)".
Apply:
-logger.log(message: "\(error)", level: .error) +logger.log(message: error.message, level: .error)As per coding guidelines
Also applies to: 186-188
Sources/AblyChat/Rooms.swift (1)
171-176: Good migration to InternalError.internallyThrownError cases and public boundary conversion preserved. One suggestion: mark DefaultRooms as @mainactor to enforce isolation of mutable state (roomStates, etc.) at type level, not just via protocol requirements.
Outside this hunk, change the class declaration to:
@MainActor internal class DefaultRooms<RoomFactory: AblyChat.RoomFactory>: Rooms { ... }As per coding guidelines
Also applies to: 348-348
Sources/AblyChat/RoomLifecycleManager.swift (1)
213-216: Migration looks correct; consider @mainactor on the class
- Correctly uses InternalError internally and ARTErrorInfo only for public-facing emissions. 👍
- Recommend marking DefaultRoomLifecycleManager as @mainactor to ensure all state mutations (roomStatus, currentOperationID, continuations) are actor-isolated even for non-protocol helpers like didReceiveChannelStateEvent.
Outside this hunk, change the class declaration to:
@MainActor internal class DefaultRoomLifecycleManager: RoomLifecycleManager { ... }As per coding guidelines
Also applies to: 396-399, 458-465, 612-620
Sources/AblyChat/Errors.swift (1)
171-177: Avoid hardcoding the status code userInfo keyCentralize "ARTErrorInfoStatusCode" to a single constant to prevent typos and ease future changes.
For example:
private let artErrorInfoStatusCodeKey = "ARTErrorInfoStatusCode" ... userInfo[artErrorInfoStatusCodeKey] = internallyThrownError.codeAndStatusCode.statusCode
📜 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.
📒 Files selected for processing (17)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift(9 hunks)Sources/AblyChat/DefaultMessageReactions.swift(1 hunks)Sources/AblyChat/DefaultMessages.swift(1 hunks)Sources/AblyChat/Errors.swift(3 hunks)Sources/AblyChat/InternalError.swift(0 hunks)Sources/AblyChat/Message.swift(2 hunks)Sources/AblyChat/PaginatedResult.swift(1 hunks)Sources/AblyChat/RoomLifecycleManager.swift(4 hunks)Sources/AblyChat/Rooms.swift(2 hunks)Tests/AblyChatTests/ChatAPITests.swift(1 hunks)Tests/AblyChatTests/DefaultMessageReactionsTests.swift(2 hunks)Tests/AblyChatTests/DefaultPresenceTests.swift(6 hunks)Tests/AblyChatTests/Helpers/Helpers.swift(3 hunks)Tests/AblyChatTests/InternalErrorTests.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtime.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift(3 hunks)
💤 Files with no reviewable changes (1)
- Sources/AblyChat/InternalError.swift
🧰 Additional context used
📓 Path-based instructions (3)
Tests/AblyChatTests/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Tests/AblyChatTests/**/*.swift: Place all tests under Tests/AblyChatTests
Use test attribution tags: @SPEC, @specOneOf(m/n), @specPartial, @specUntested, @specNotApplicable with appropriate spec IDs and explanations
For Swift Testing #expect(throws:) with typed errors, move typed-throw code into a separate non-typed-throw function (until Xcode 16.3+)
Files:
Tests/AblyChatTests/ChatAPITests.swiftTests/AblyChatTests/DefaultMessageReactionsTests.swiftTests/AblyChatTests/Mocks/MockRealtimeChannel.swiftTests/AblyChatTests/DefaultPresenceTests.swiftTests/AblyChatTests/Helpers/Helpers.swiftTests/AblyChatTests/Mocks/MockRealtime.swiftTests/AblyChatTests/Mocks/MockRoomLifecycleManager.swiftTests/AblyChatTests/InternalErrorTests.swift
Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Sources/**/*.swift: Prefer protocol-based design; expose SDK functionality via protocols and prefer opaque return types (some Protocol) over existential any Protocol
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API should use typed throws with ARTErrorInfo
Use InternalError internally and convert to ARTErrorInfo at public API boundaries
Test-only APIs in the library must be prefixed testsOnly_ and wrapped in #if DEBUG
Reference Chat SDK spec items in implementation comments (e.g., // @SPEC CHA-RL3g)
For public structs exposed by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
For Task, CheckedContinuation, and AsyncThrowingStream (which don’t support typed errors), use Result and call .get() to bridge typed errors
Avoid Dictionary.mapValues for typed-throwing transformations; use ablyChat_mapValuesWithTypedThrow
When the compiler struggles with typed throws, explicitly annotate do throws(InternalError) blocks
Specify error types in closures, e.g., try items.map { jsonValue throws(InternalError) in … }
Files:
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swiftSources/AblyChat/DefaultMessages.swiftSources/AblyChat/PaginatedResult.swiftSources/AblyChat/Rooms.swiftSources/AblyChat/DefaultMessageReactions.swiftSources/AblyChat/RoomLifecycleManager.swiftSources/AblyChat/Message.swiftSources/AblyChat/Errors.swift
Tests/AblyChatTests/Mocks/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Place mock implementations under Tests/AblyChatTests/Mocks
Files:
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swiftTests/AblyChatTests/Mocks/MockRealtime.swiftTests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
🧬 Code graph analysis (8)
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Sources/AblyChat/DefaultMessages.swift (1)
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
failure(71-73)
Tests/AblyChatTests/DefaultPresenceTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Sources/AblyChat/DefaultMessageReactions.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Sources/AblyChat/RoomLifecycleManager.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Sources/AblyChat/Message.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Tests/AblyChatTests/InternalErrorTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(164-185)
Sources/AblyChat/Errors.swift (3)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (2)
attach(349-363)detach(365-379)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (2)
attach(94-102)detach(108-116)Sources/AblyChat/Room.swift (2)
attach(377-383)detach(386-392)
⏰ 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, macOS (Xcode 26.0)
- GitHub Check: Example app, tvOS (Xcode 26.0)
- GitHub Check: Example app, iOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, macOS (Xcode 26.0) - GitHub Check: SPM (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, iOS (Xcode 26.0) - GitHub Check: Xcode, macOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, tvOS (Xcode 26.0) - GitHub Check: Xcode, iOS (Xcode 26.0)
- GitHub Check: Xcode, tvOS (Xcode 26.0)
- GitHub Check: SPM,
releaseconfiguration (Xcode 26.0) - GitHub Check: check-documentation
🔇 Additional comments (14)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (1)
167-167: LGTM! Consistent error wrapping pattern.The refactoring consistently wraps ably-cocoa errors with
InternalError.ablyCocoa(error)across all adapter methods. This aligns with the internal error handling strategy and maintains typed throws throughout the internal API.Also applies to: 237-237, 255-255, 271-271, 287-287, 303-303, 361-361, 377-377, 393-393
Tests/AblyChatTests/Mocks/MockRealtime.swift (1)
47-47: LGTM! Mock behavior aligned with production code.The mock correctly wraps the error with
InternalError.ablyCocoa(error), maintaining consistency with the production adapter implementations.Sources/AblyChat/PaginatedResult.swift (1)
38-38: LGTM! Consistent error wrapping.The change correctly wraps the ably-cocoa error with
InternalError.ablyCocoa(error), aligning with the internal error handling refactor.Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
85-85: LGTM! Consistent mock implementation.The mock correctly throws
InternalError.ablyCocoa(error), matching the behavior of production adapters.Sources/AblyChat/DefaultMessageReactions.swift (1)
44-44: LGTM! Correct boundary conversion.The change properly converts the internal error to
ARTErrorInfoat the public API boundary usingtoARTErrorInfo(), following the coding guidelines.Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift (1)
33-33: LGTM! Consistent mock implementation.All catch blocks correctly wrap errors with
InternalError.ablyCocoa(error), maintaining consistency with the production code pattern.Also applies to: 45-45, 79-79
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (1)
78-78: LGTM! Test expectations correctly updated.The test expectations now verify that the thrown
ARTErrorInfomatches the expected error generated viaInternalError.internallyThrown(...).toARTErrorInfo(), correctly testing the public API boundary conversion.Also applies to: 98-98
Tests/AblyChatTests/ChatAPITests.swift (1)
24-29: Matching onInternalErroris correct:sendMessageis internal and throwsInternalError.Likely an incorrect or invalid review comment.
Sources/AblyChat/Message.swift (1)
227-227: Good switch to internal error + conversionPublic API still throws ARTErrorInfo; internal source uses InternalError.internallyThrown with correct badRequest mapping. LGTM.
Also applies to: 248-248, 253-253
Tests/AblyChatTests/DefaultPresenceTests.swift (1)
87-88: Tests align with new InternalError → ARTErrorInfo flowUpdated construction/expectations look correct and consistent with toARTErrorInfo() behavior. 👍
Also applies to: 118-119, 194-195, 225-226, 330-331, 379-380
Tests/AblyChatTests/InternalErrorTests.swift (1)
9-9: LGTMCovers both ablyCocoa passthrough and internallyThrown → ErrorInfo mapping to badRequest.
Also applies to: 17-17
Tests/AblyChatTests/Helpers/Helpers.swift (1)
13-15: Helpers updated correctly for InternalError.ablyCocoa and new casesPattern matches and enumCase mapping look consistent with the refactor. 👍
Also applies to: 36-41, 46-48, 52-56
Sources/AblyChat/Errors.swift (2)
163-184: toARTErrorInfo implementation looks solidCorrectly sets domain, code, status (via userInfo), description, and underlying cause. Matches the new error model. 👍
222-259: Mapping of codes/status to spec looks correct
- presenceOperationRequiresRoomAttach → 400
- roomTransitionedToInvalidStateForPresenceOperation → 500
- discontinuity → 500
- moderation/beforePublish rejections → 422
- others → 400
LGTM.
7105ce6 to
4751614
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
Sources/AblyChat/Errors.swift (2)
162-218: Internal mapping reads well; add lightweight tests for code/status pairsThe code/status mapping and spec notes are clear. Consider unit tests exercising each case in codeAndStatusCode to prevent regressions when adding new cases.
Also applies to: 220-258
350-378: Uniform conversion helpers: consider naming/placement tweakThe toInternalError() helpers are fine. Optionally rename to asInternalError() for symmetry with other “asX” conversions and group them under a single extension file to reduce scattering.
Tests/AblyChatTests/DefaultPresenceTests.swift (1)
87-88: Deduplicate error construction in testsExtract a small helper (e.g., makePresenceInvalidStateError(status: cause:)) to build these ARTErrorInfo values and reduce repetition.
Also applies to: 118-119, 194-195, 225-226, 330-331, 379-380
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (1)
167-168: Prefer typed catch for clarity; optional helper to DRYUse
catch let error as ARTErrorInfo { throw InternalError.ablyCocoa(error) }for explicitness. Optionally extract a small helper to wrapResult<T, ARTErrorInfo>.get()intothrows(InternalError)to avoid repeating the catch/wrap pattern.Also applies to: 237-238, 255-256, 271-272, 287-288, 303-304, 361-362, 377-378, 393-394
Sources/AblyChat/RoomLifecycleManager.swift (1)
19-22: Update docs: replace ChatError referencesDocs still mention ChatError-derived ARTErrorInfo. Update to reflect InternalError.internallyThrown(...).toARTErrorInfo() or reference ErrorCode where appropriate.
📜 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.
📒 Files selected for processing (17)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift(9 hunks)Sources/AblyChat/DefaultMessageReactions.swift(1 hunks)Sources/AblyChat/DefaultMessages.swift(1 hunks)Sources/AblyChat/Errors.swift(2 hunks)Sources/AblyChat/InternalError.swift(0 hunks)Sources/AblyChat/Message.swift(2 hunks)Sources/AblyChat/PaginatedResult.swift(1 hunks)Sources/AblyChat/RoomLifecycleManager.swift(4 hunks)Sources/AblyChat/Rooms.swift(2 hunks)Tests/AblyChatTests/ChatAPITests.swift(1 hunks)Tests/AblyChatTests/DefaultMessageReactionsTests.swift(2 hunks)Tests/AblyChatTests/DefaultPresenceTests.swift(6 hunks)Tests/AblyChatTests/Helpers/Helpers.swift(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtime.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift(3 hunks)
💤 Files with no reviewable changes (1)
- Sources/AblyChat/InternalError.swift
🚧 Files skipped from review as they are similar to previous changes (8)
- Sources/AblyChat/Message.swift
- Sources/AblyChat/DefaultMessageReactions.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Tests/AblyChatTests/DefaultMessageReactionsTests.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Sources/AblyChat/DefaultMessages.swift
- Sources/AblyChat/PaginatedResult.swift
- Tests/AblyChatTests/Mocks/MockRealtime.swift
🧰 Additional context used
📓 Path-based instructions (3)
Tests/AblyChatTests/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Tests/AblyChatTests/**/*.swift: Place all tests under Tests/AblyChatTests
Use test attribution tags: @SPEC, @specOneOf(m/n), @specPartial, @specUntested, @specNotApplicable with appropriate spec IDs and explanations
For Swift Testing #expect(throws:) with typed errors, move typed-throw code into a separate non-typed-throw function (until Xcode 16.3+)
Files:
Tests/AblyChatTests/InternalErrorTests.swiftTests/AblyChatTests/Helpers/Helpers.swiftTests/AblyChatTests/DefaultPresenceTests.swiftTests/AblyChatTests/Mocks/MockRealtimeChannel.swift
Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Sources/**/*.swift: Prefer protocol-based design; expose SDK functionality via protocols and prefer opaque return types (some Protocol) over existential any Protocol
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API should use typed throws with ARTErrorInfo
Use InternalError internally and convert to ARTErrorInfo at public API boundaries
Test-only APIs in the library must be prefixed testsOnly_ and wrapped in #if DEBUG
Reference Chat SDK spec items in implementation comments (e.g., // @SPEC CHA-RL3g)
For public structs exposed by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
For Task, CheckedContinuation, and AsyncThrowingStream (which don’t support typed errors), use Result and call .get() to bridge typed errors
Avoid Dictionary.mapValues for typed-throwing transformations; use ablyChat_mapValuesWithTypedThrow
When the compiler struggles with typed throws, explicitly annotate do throws(InternalError) blocks
Specify error types in closures, e.g., try items.map { jsonValue throws(InternalError) in … }
Files:
Sources/AblyChat/RoomLifecycleManager.swiftSources/AblyChat/Errors.swiftSources/AblyChat/Rooms.swiftSources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift
Tests/AblyChatTests/Mocks/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Place mock implementations under Tests/AblyChatTests/Mocks
Files:
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift
🧬 Code graph analysis (4)
Tests/AblyChatTests/InternalErrorTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/RoomLifecycleManager.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/Errors.swift (2)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (2)
attach(349-363)detach(365-379)Sources/AblyChat/Room.swift (2)
attach(377-383)detach(386-392)
Tests/AblyChatTests/DefaultPresenceTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
⏰ 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, macOS (Xcode 26.0)
- GitHub Check: Example app, iOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, macOS (Xcode 26.0) - GitHub Check: Example app, tvOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, tvOS (Xcode 26.0) - GitHub Check: Xcode,
releaseconfiguration, iOS (Xcode 26.0) - GitHub Check: Xcode, macOS (Xcode 26.0)
- GitHub Check: Xcode, tvOS (Xcode 26.0)
- GitHub Check: Xcode, iOS (Xcode 26.0)
- GitHub Check: SPM,
releaseconfiguration (Xcode 26.0) - GitHub Check: SPM (Xcode 26.0)
🔇 Additional comments (16)
Sources/AblyChat/Errors.swift (3)
157-161: message passthrough looks goodSimple and useful for logs; no change requested.
281-318: Descriptions are user‑friendly and specificGood UX text; no change requested.
320-344: Cause propagation is sensibleOptional causes are surfaced only when meaningful. LGTM.
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
85-85: Aligns with InternalError modelWrapping ARTErrorInfo via .ablyCocoa is correct for typed throws.
Tests/AblyChatTests/InternalErrorTests.swift (2)
9-9: LGTM: ablyCocoa passthrough verifiedKeeps identity of ARTErrorInfo instance as expected.
17-17: LGTM: non‑ARTErrorInfo conversion validatedAsserts code mapping and message content. Solid coverage.
Sources/AblyChat/RoomLifecycleManager.swift (4)
213-214: Discontinuity emission aligned with new modelWrapping cause and producing ARTErrorInfo via InternalError is correct.
395-399: Correct error surfaces for ATTACH preconditionsThrowing InternalError.internallyThrown(.roomIsReleasing/.roomIsReleased) matches spec and typed throws.
458-465: Correct error surfaces for DETACH preconditionsMatches spec mapping; keep as is.
612-613: Presence wait: correct invalid-state errorsGood use of internallyThrown variants to signal invalid states with appropriate status codes.
Also applies to: 619-620
Sources/AblyChat/Rooms.swift (2)
171-176: LGTM!The error construction correctly uses
InternalError.internallyThrownwithin the typed-throw block and will be converted toARTErrorInfoat the public API boundary (line 272), aligning with the coding guidelines.
348-348: LGTM!The error construction correctly passes
InternalErrortofailCreation, which flows through theResult<..., InternalError>type and is properly bridged via.get()(line 265) before conversion toARTErrorInfoat the public API boundary (line 272).Tests/AblyChatTests/Helpers/Helpers.swift (4)
5-39: LGTM!The dual handling of both
ARTErrorInfoandInternalError.internallyThrownis appropriate for a test helper that needs to verify errors at different layers of abstraction. The logic correctly checks for equality of codes, status codes, causes, and optional messages in both branches.
41-47: LGTM!The change from
.errorInfoto.ablyCocoacorrectly reflects the new internal error handling model introduced by this PR.
49-64: LGTM!The simplification to
jsonValueDecodingErrorandothercases appropriately reduces complexity while maintaining the necessary distinctions for test verification.
66-72: LGTM!The new helper function provides a clean way to verify error cases in tests. Implementation is straightforward and correct.
4751614 to
ac30055
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Sources/AblyChat/Errors.swift (1)
140-141: Refactor: Extract hardcoded userInfo key to a constant.The string literal
"ARTErrorInfoStatusCode"should be extracted to a private static constant to avoid typos and improve maintainability, as noted in the past review comment.Apply this diff to extract the constant:
+ private static let artErrorInfoStatusCodeKey = "ARTErrorInfoStatusCode" + /// Returns the error that this should be converted to when exposed via the SDK's public API. internal func toARTErrorInfo() -> ARTErrorInfo { switch self { case let .ablyCocoa(errorInfo): return errorInfo case let .internallyThrown(internallyThrownError): var userInfo: [String: Any] = [:] // TODO: copied and pasted from implementation of -[ARTErrorInfo createWithCode:status:message:requestId:] because there's no way to pass domain; revisit in https://github.com/ably-labs/ably-chat-swift/issues/32. Also the ARTErrorInfoStatusCode variable in ably-cocoa is not public. - userInfo["ARTErrorInfoStatusCode"] = internallyThrownError.codeAndStatusCode.statusCode + userInfo[Self.artErrorInfoStatusCodeKey] = internallyThrownError.codeAndStatusCode.statusCode userInfo[NSLocalizedDescriptionKey] = internallyThrownError.localizedDescription
🧹 Nitpick comments (5)
Sources/AblyChat/DefaultMessages.swift (1)
3-3: Consider marking DefaultMessages as @mainactorThis type holds mutable state (currentSubscriptionPoint, subscriptionPoints) and interacts with @MainActor-only protocols. Marking the class @mainactor would align with “isolate mutable state to the main actor.”
As per coding guidelines
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (1)
78-79: Assertions updated to new InternalError → ARTErrorInfo mappingLooks correct and aligned with the refactor.
To make tests less brittle to userInfo/message changes, consider asserting
domainandcode(and optionallycause) rather than full equality. Example:
#expect(thrownError?.domain == expected.domain && thrownError?.code == expected.code)Also applies to: 98-99
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (1)
167-169: Consistent wrapping of ARTErrorInfo via .ablyCocoa after Result.get()The mapping is uniform and matches the new error model. LGTM.
- Consider extracting a small helper to reduce duplication, e.g.,
awaitFromCallback { (resume: (Result<T, ARTErrorInfo>) -> Void) in ... }that returnsTor throwsInternalError.ablyCocoa.- Optionally replace
_inCheckedContinuation<..., _>withNeverfor clarity and tool support.Also applies to: 237-239, 255-257, 271-273, 287-289, 303-305, 361-363, 377-379, 393-395
Tests/AblyChatTests/Mocks/MockRealtime.swift (1)
47-48: Use typed catch for ARTErrorInfo when wrapping with .ablyCocoaPrefer catching
ARTErrorInfoto ensure only Ably Cocoa errors are wrapped:- } catch { - throw InternalError.ablyCocoa(error) + } catch let error as ARTErrorInfo { + throw InternalError.ablyCocoa(error) + } catch { + // Unexpected non-ARTErrorInfo; keep context + throw InternalError.internallyThrown(.other(.unexpected(cause: error))) }Sources/AblyChat/Errors.swift (1)
140-141: Update misleading TODO comment.The TODO comment states "there's no way to pass domain" but the code on line 150 explicitly passes the
domainparameter toARTErrorInfo. This comment appears to be outdated and should be revised or removed.Consider updating the TODO to:
- // TODO: copied and pasted from implementation of -[ARTErrorInfo createWithCode:status:message:requestId:] because there's no way to pass domain; revisit in https://github.com/ably-labs/ably-chat-swift/issues/32. Also the ARTErrorInfoStatusCode variable in ably-cocoa is not public. + // TODO: copied and pasted from implementation of -[ARTErrorInfo createWithCode:status:message:requestId:]; revisit in https://github.com/ably-labs/ably-chat-swift/issues/32. Also the ARTErrorInfoStatusCode variable in ably-cocoa is not public.
📜 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.
📒 Files selected for processing (17)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift(9 hunks)Sources/AblyChat/DefaultMessageReactions.swift(1 hunks)Sources/AblyChat/DefaultMessages.swift(1 hunks)Sources/AblyChat/Errors.swift(2 hunks)Sources/AblyChat/InternalError.swift(0 hunks)Sources/AblyChat/Message.swift(2 hunks)Sources/AblyChat/PaginatedResult.swift(1 hunks)Sources/AblyChat/RoomLifecycleManager.swift(5 hunks)Sources/AblyChat/Rooms.swift(2 hunks)Tests/AblyChatTests/ChatAPITests.swift(1 hunks)Tests/AblyChatTests/DefaultMessageReactionsTests.swift(2 hunks)Tests/AblyChatTests/DefaultPresenceTests.swift(6 hunks)Tests/AblyChatTests/Helpers/Helpers.swift(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtime.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift(3 hunks)
💤 Files with no reviewable changes (1)
- Sources/AblyChat/InternalError.swift
🚧 Files skipped from review as they are similar to previous changes (8)
- Sources/AblyChat/PaginatedResult.swift
- Tests/AblyChatTests/DefaultPresenceTests.swift
- Sources/AblyChat/DefaultMessageReactions.swift
- Tests/AblyChatTests/InternalErrorTests.swift
- Sources/AblyChat/Message.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift
🧰 Additional context used
📓 Path-based instructions (3)
Tests/AblyChatTests/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Tests/AblyChatTests/**/*.swift: Place all tests under Tests/AblyChatTests
Use test attribution tags: @SPEC, @specOneOf(m/n), @specPartial, @specUntested, @specNotApplicable with appropriate spec IDs and explanations
For Swift Testing #expect(throws:) with typed errors, move typed-throw code into a separate non-typed-throw function (until Xcode 16.3+)
Files:
Tests/AblyChatTests/Helpers/Helpers.swiftTests/AblyChatTests/DefaultMessageReactionsTests.swiftTests/AblyChatTests/Mocks/MockRealtime.swift
Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Sources/**/*.swift: Prefer protocol-based design; expose SDK functionality via protocols and prefer opaque return types (some Protocol) over existential any Protocol
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API should use typed throws with ARTErrorInfo
Use InternalError internally and convert to ARTErrorInfo at public API boundaries
Test-only APIs in the library must be prefixed testsOnly_ and wrapped in #if DEBUG
Reference Chat SDK spec items in implementation comments (e.g., // @SPEC CHA-RL3g)
For public structs exposed by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
For Task, CheckedContinuation, and AsyncThrowingStream (which don’t support typed errors), use Result and call .get() to bridge typed errors
Avoid Dictionary.mapValues for typed-throwing transformations; use ablyChat_mapValuesWithTypedThrow
When the compiler struggles with typed throws, explicitly annotate do throws(InternalError) blocks
Specify error types in closures, e.g., try items.map { jsonValue throws(InternalError) in … }
Files:
Sources/AblyChat/DefaultMessages.swiftSources/AblyChat/RoomLifecycleManager.swiftSources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swiftSources/AblyChat/Errors.swiftSources/AblyChat/Rooms.swift
Tests/AblyChatTests/Mocks/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Place mock implementations under Tests/AblyChatTests/Mocks
Files:
Tests/AblyChatTests/Mocks/MockRealtime.swift
🧬 Code graph analysis (4)
Tests/AblyChatTests/DefaultMessageReactionsTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/DefaultMessages.swift (1)
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
failure(71-73)
Sources/AblyChat/RoomLifecycleManager.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/Errors.swift (3)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (2)
attach(349-363)detach(365-379)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (2)
attach(94-102)detach(108-116)Sources/AblyChat/Room.swift (2)
attach(377-383)detach(386-392)
⏰ 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: Xcode,
releaseconfiguration, macOS (Xcode 26.0) - GitHub Check: Example app, tvOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, 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,
releaseconfiguration, iOS (Xcode 26.0) - GitHub Check: SPM (Xcode 26.0)
- GitHub Check: Xcode, macOS (Xcode 26.0)
- GitHub Check: Xcode, iOS (Xcode 26.0)
- GitHub Check: SPM,
releaseconfiguration (Xcode 26.0) - GitHub Check: check-documentation
🔇 Additional comments (12)
Sources/AblyChat/DefaultMessages.swift (1)
182-183: Good migration to InternalError.internallyThrown in continuation failuresThe new error construction is consistent with the refactor and preserves control flow. LGTM.
Also applies to: 186-189
Sources/AblyChat/RoomLifecycleManager.swift (2)
19-22: Doc updates reflect new InternalError usage for presence checksAccurately documents the new error shapes. LGTM.
213-216: Room lifecycle now throws InternalError.internallyThrown in all branchesError construction is consistent, and discontinuity emission converts to ARTErrorInfo at the boundary. LGTM.
Also applies to: 396-399, 462-465, 610-613, 618-620
Sources/AblyChat/Rooms.swift (2)
171-176: LGTM! Error construction aligns with new internal model.The change correctly replaces the old error construction pattern with
InternalError.internallyThrown(.inconsistentRoomOptions(...)). This will be converted toARTErrorInfoat the public API boundary (line 272), maintaining the expected public API contract.
348-348: LGTM! Consistent error construction pattern.The change correctly uses
InternalError.internallyThrown(.roomReleasedBeforeOperationCompleted)instead of constructing anARTErrorInfoand converting it. This aligns with the new internal error handling model.Tests/AblyChatTests/Helpers/Helpers.swift (4)
5-39: LGTM! Test helper correctly handles both error paths.The updated
isChatErrorfunction properly validates bothARTErrorInfo(in the chat domain) andInternalError.internallyThrowncases, checking code, statusCode, cause, and optional message for both paths. This aligns with the new internal error model while maintaining test coverage.
41-47: LGTM! Updated to match new InternalError case.Correctly updated to match against
InternalError.ablyCocoainstead of the previousInternalError.errorInfocase.
49-64: LGTM! Simplified enum aligns with refactored error model.The
InternalError.Caseenum is now simpler, with onlyjsonValueDecodingErrorandothercases. TheenumCasecomputed property correctly mapsInternalError.internallyThrown(.other(.jsonValueDecodingError))to the specific case and everything else to.other.
66-72: LGTM! Useful helper for case-based error checking.The new
isInternalErrorWithCasehelper provides a clean way to check if an error is anInternalErrorwith a specific case. Implementation is straightforward and correct.Sources/AblyChat/Errors.swift (3)
123-161: LGTM! New internal error model is well-structured.The
InternalErrorenum withablyCocoaandinternallyThrowncases provides a clean separation between ably-cocoa errors and Chat SDK-specific errors. ThetoARTErrorInfo()conversion method correctly handles both paths:
ablyCocoa: Returns theARTErrorInfodirectlyinternallyThrown: Constructs anARTErrorInfowith proper domain, code, userInfo (including statusCode and localizedDescription), and optional causeThe computed
messageproperty is a useful addition for logging.
165-346: LGTM! Comprehensive internal error model with proper metadata.The
InternallyThrownenum provides a well-structured set of internal error cases with associated metadata. Key strengths:
- Each case has meaningful associated values where needed (e.g.,
requestedandexistingoptions forinconsistentRoomOptions)- The
ErrorCodeAndStatusCodeenum properly distinguishes between fixed and variable status codes- The
codeAndStatusCode,localizedDescription, andcausecomputed properties provide consistent error information- Helper methods like
descriptionOfFeatureand theAttachOrDetachenum improve code clarity- Error messages are descriptive and actionable
The
Othernested enum provides a clean way to wrap various external error types.
348-378: LGTM! Convenient conversion extensions for internal error types.The extensions on
ChatAPI.ChatError,HeadersValue.JSONDecodingError,JSONValueDecodingError,PaginatedResultError, andDefaultMessages.MessagesErrorprovide a clean and consistent way to convert these types toInternalError. The pattern of wrapping them inInternalError.internallyThrown(.other(...))maintains uniformity across the error handling model.
ac30055 to
8d67745
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Sources/AblyChat/Errors.swift (1)
139-152: Centralize the status-code key and update the outdated TODOWe’re still hard-coding
"ARTErrorInfoStatusCode"and the adjacent TODO claims we can’t pass a domain, even though we now do. Please extract that key into a single private constant (or nested enum) and refresh/remove the TODO so it reflects the current reality. This was called out in the earlier review and is still outstanding.
🧹 Nitpick comments (1)
Sources/AblyChat/PaginatedResult.swift (1)
42-44: Consider updating for consistency with the new error pattern.Line 43 still uses the older
.toInternalError()method forPaginatedResultError. For consistency with the new pattern introduced on line 38, consider refactoring to use.internallyThrown():- continuation.resume(returning: .failure(PaginatedResultError.noErrorWithInvalidResponse.toInternalError())) + continuation.resume(returning: .failure(.internallyThrown(PaginatedResultError.noErrorWithInvalidResponse)))
📜 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.
📒 Files selected for processing (17)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift(9 hunks)Sources/AblyChat/DefaultMessageReactions.swift(1 hunks)Sources/AblyChat/DefaultMessages.swift(1 hunks)Sources/AblyChat/Errors.swift(2 hunks)Sources/AblyChat/InternalError.swift(0 hunks)Sources/AblyChat/Message.swift(2 hunks)Sources/AblyChat/PaginatedResult.swift(1 hunks)Sources/AblyChat/RoomLifecycleManager.swift(5 hunks)Sources/AblyChat/Rooms.swift(2 hunks)Tests/AblyChatTests/ChatAPITests.swift(1 hunks)Tests/AblyChatTests/DefaultMessageReactionsTests.swift(2 hunks)Tests/AblyChatTests/DefaultPresenceTests.swift(6 hunks)Tests/AblyChatTests/Helpers/Helpers.swift(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtime.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift(3 hunks)
💤 Files with no reviewable changes (1)
- Sources/AblyChat/InternalError.swift
🚧 Files skipped from review as they are similar to previous changes (10)
- Tests/AblyChatTests/DefaultPresenceTests.swift
- Sources/AblyChat/Message.swift
- Sources/AblyChat/DefaultMessageReactions.swift
- Sources/AblyChat/RoomLifecycleManager.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Tests/AblyChatTests/Mocks/MockRealtime.swift
- Sources/AblyChat/DefaultMessages.swift
- Tests/AblyChatTests/InternalErrorTests.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Tests/AblyChatTests/DefaultMessageReactionsTests.swift
🧰 Additional context used
📓 Path-based instructions (3)
Tests/AblyChatTests/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Tests/AblyChatTests/**/*.swift: Place all tests under Tests/AblyChatTests
Use test attribution tags: @SPEC, @specOneOf(m/n), @specPartial, @specUntested, @specNotApplicable with appropriate spec IDs and explanations
For Swift Testing #expect(throws:) with typed errors, move typed-throw code into a separate non-typed-throw function (until Xcode 16.3+)
Files:
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swiftTests/AblyChatTests/Helpers/Helpers.swift
Tests/AblyChatTests/Mocks/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Place mock implementations under Tests/AblyChatTests/Mocks
Files:
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift
Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Sources/**/*.swift: Prefer protocol-based design; expose SDK functionality via protocols and prefer opaque return types (some Protocol) over existential any Protocol
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API should use typed throws with ARTErrorInfo
Use InternalError internally and convert to ARTErrorInfo at public API boundaries
Test-only APIs in the library must be prefixed testsOnly_ and wrapped in #if DEBUG
Reference Chat SDK spec items in implementation comments (e.g., // @SPEC CHA-RL3g)
For public structs exposed by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
For Task, CheckedContinuation, and AsyncThrowingStream (which don’t support typed errors), use Result and call .get() to bridge typed errors
Avoid Dictionary.mapValues for typed-throwing transformations; use ablyChat_mapValuesWithTypedThrow
When the compiler struggles with typed throws, explicitly annotate do throws(InternalError) blocks
Specify error types in closures, e.g., try items.map { jsonValue throws(InternalError) in … }
Files:
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swiftSources/AblyChat/Rooms.swiftSources/AblyChat/PaginatedResult.swiftSources/AblyChat/Errors.swift
🧬 Code graph analysis (2)
Sources/AblyChat/PaginatedResult.swift (1)
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
failure(71-73)
Sources/AblyChat/Errors.swift (5)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (2)
attach(349-363)detach(365-379)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (2)
attach(94-102)detach(108-116)Example/AblyChatExample/Mocks/MockClients.swift (2)
attach(85-87)detach(89-91)Tests/AblyChatTests/Mocks/MockRoom.swift (2)
attach(47-49)detach(51-53)Sources/AblyChat/Room.swift (2)
attach(377-383)detach(386-392)
⏰ 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,
releaseconfiguration, iOS (Xcode 26.0) - GitHub Check: Example app, macOS (Xcode 26.0)
- GitHub Check: Example app, tvOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, tvOS (Xcode 26.0) - GitHub Check: Example app, iOS (Xcode 26.0)
- GitHub Check: Xcode, tvOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, macOS (Xcode 26.0) - GitHub Check: Xcode, iOS (Xcode 26.0)
- GitHub Check: Xcode, macOS (Xcode 26.0)
- GitHub Check: SPM,
releaseconfiguration (Xcode 26.0) - GitHub Check: SPM (Xcode 26.0)
🔇 Additional comments (1)
Sources/AblyChat/PaginatedResult.swift (1)
38-38: LGTM! Correctly adopts the new internal error pattern.The change from
error.toInternalError()to.ablyCocoa(error)properly wraps theARTErrorInfousing the new standardized pattern, aligning with the PR's goal of consolidating internal error handling.
This is a refactor that combines the ChatError and InternalError types (having three different error types — ARTErrorInfo, InternalError, and ChatError — at play in the codebase was getting rather confusing!) This is groundwork for some further error-related changes.
8d67745 to
3891cb9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
Sources/AblyChat/Errors.swift (1)
139-153: Centralize statusCode userInfo key and update stale TODO.
- Replace the hard-coded
"ARTErrorInfoStatusCode"with a private static constant to avoid typos.- The TODO about “no way to pass domain” is outdated; you are passing
domain:. Reword to reflect only the statusCode-key workaround.Example:
internal func toARTErrorInfo() -> ARTErrorInfo { switch self { case let .fromAblyCocoa(errorInfo): return errorInfo case let .internallyThrown(internallyThrownError): - var userInfo: [String: Any] = [:] - // TODO: copied and pasted from implementation of -[ARTErrorInfo createWithCode:status:message:requestId:] because there’s no way to pass domain; revisit in https://github.com/ably-labs/ably-chat-swift/issues/32. Also the ARTErrorInfoStatusCode variable in ably-cocoa is not public. - userInfo["ARTErrorInfoStatusCode"] = internallyThrownError.codeAndStatusCode.statusCode + var userInfo: [String: Any] = [:] + // TODO: ably-cocoa does not expose a public constant for the statusCode userInfo key; track in https://github.com/ably/ably-cocoa/issues/XXXX. + userInfo[Self.artErrorInfoStatusCodeKey] = internallyThrownError.codeAndStatusCode.statusCode userInfo[NSLocalizedDescriptionKey] = internallyThrownError.localizedDescription ... return ARTErrorInfo( domain: errorDomain, code: internallyThrownError.codeAndStatusCode.code.rawValue, userInfo: userInfo, ) } } + +// Near InternalError +private extension InternalError { + static let artErrorInfoStatusCodeKey = "ARTErrorInfoStatusCode" +}Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (1)
167-168: Downcast caught errors to ARTErrorInfo before wrapping (compile-time fix).In these catch blocks,
errorisany Error, butInternalError.fromAblyCocoaexpectsARTErrorInfo. This won’t compile without a downcast. Since.get()throwsARTErrorInfo, use a typed catch.Apply this at each site:
- } catch { - throw InternalError.fromAblyCocoa(error) + } catch let ablyError as ARTErrorInfo { + throw InternalError.fromAblyCocoa(ablyError) }Run to locate any remaining instances:
#!/bin/bash rg -n -C2 -P 'catch\s*\{\s*throw\s+InternalError\.fromAblyCocoa\(\s*error\s*\)\s*\}' --glob '!**/DerivedData/**'Also applies to: 237-238, 255-256, 271-272, 287-288, 303-304, 361-362, 377-378, 393-394
🧹 Nitpick comments (2)
Tests/AblyChatTests/InternalErrorTests.swift (1)
17-17: LGTM: internallyThrown path validated; consider attribution tags.Assertion covers code/status and message content. Please add test attribution tags per guidelines (e.g., @specNotApplicable with a brief note).
Tests/AblyChatTests/Helpers/Helpers.swift (1)
14-20: Consider simplifying the message check.The closure for checking the optional message works correctly but could be more concise.
Apply this diff to simplify:
- && { - guard let message else { - return true - } - - return ablyError.message == message - }() + && (message == nil || ablyError.message == message)
📜 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.
📒 Files selected for processing (17)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift(9 hunks)Sources/AblyChat/DefaultMessageReactions.swift(1 hunks)Sources/AblyChat/DefaultMessages.swift(1 hunks)Sources/AblyChat/Errors.swift(2 hunks)Sources/AblyChat/InternalError.swift(0 hunks)Sources/AblyChat/Message.swift(2 hunks)Sources/AblyChat/PaginatedResult.swift(1 hunks)Sources/AblyChat/RoomLifecycleManager.swift(5 hunks)Sources/AblyChat/Rooms.swift(2 hunks)Tests/AblyChatTests/ChatAPITests.swift(1 hunks)Tests/AblyChatTests/DefaultMessageReactionsTests.swift(2 hunks)Tests/AblyChatTests/DefaultPresenceTests.swift(6 hunks)Tests/AblyChatTests/Helpers/Helpers.swift(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtime.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift(3 hunks)
💤 Files with no reviewable changes (1)
- Sources/AblyChat/InternalError.swift
🚧 Files skipped from review as they are similar to previous changes (5)
- Sources/AblyChat/PaginatedResult.swift
- Tests/AblyChatTests/DefaultPresenceTests.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Sources/AblyChat/Rooms.swift
- Tests/AblyChatTests/DefaultMessageReactionsTests.swift
🧰 Additional context used
📓 Path-based instructions (3)
Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Sources/**/*.swift: Prefer protocol-based design; expose SDK functionality via protocols and prefer opaque return types (some Protocol) over existential any Protocol
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API should use typed throws with ARTErrorInfo
Use InternalError internally and convert to ARTErrorInfo at public API boundaries
Test-only APIs in the library must be prefixed testsOnly_ and wrapped in #if DEBUG
Reference Chat SDK spec items in implementation comments (e.g., // @SPEC CHA-RL3g)
For public structs exposed by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
For Task, CheckedContinuation, and AsyncThrowingStream (which don’t support typed errors), use Result and call .get() to bridge typed errors
Avoid Dictionary.mapValues for typed-throwing transformations; use ablyChat_mapValuesWithTypedThrow
When the compiler struggles with typed throws, explicitly annotate do throws(InternalError) blocks
Specify error types in closures, e.g., try items.map { jsonValue throws(InternalError) in … }
Files:
Sources/AblyChat/Message.swiftSources/AblyChat/RoomLifecycleManager.swiftSources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swiftSources/AblyChat/DefaultMessageReactions.swiftSources/AblyChat/DefaultMessages.swiftSources/AblyChat/Errors.swift
Tests/AblyChatTests/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Tests/AblyChatTests/**/*.swift: Place all tests under Tests/AblyChatTests
Use test attribution tags: @SPEC, @specOneOf(m/n), @specPartial, @specUntested, @specNotApplicable with appropriate spec IDs and explanations
For Swift Testing #expect(throws:) with typed errors, move typed-throw code into a separate non-typed-throw function (until Xcode 16.3+)
Files:
Tests/AblyChatTests/ChatAPITests.swiftTests/AblyChatTests/Mocks/MockRealtime.swiftTests/AblyChatTests/Mocks/MockRealtimeChannel.swiftTests/AblyChatTests/Helpers/Helpers.swiftTests/AblyChatTests/InternalErrorTests.swift
Tests/AblyChatTests/Mocks/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Place mock implementations under Tests/AblyChatTests/Mocks
Files:
Tests/AblyChatTests/Mocks/MockRealtime.swiftTests/AblyChatTests/Mocks/MockRealtimeChannel.swift
🧬 Code graph analysis (6)
Sources/AblyChat/Message.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/RoomLifecycleManager.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/DefaultMessageReactions.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/DefaultMessages.swift (1)
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
failure(71-73)
Tests/AblyChatTests/InternalErrorTests.swift (1)
Sources/AblyChat/Errors.swift (1)
toARTErrorInfo(134-155)
Sources/AblyChat/Errors.swift (2)
Sources/AblyChat/AblyCocoaExtensions/InternalAblyCocoaTypes.swift (2)
attach(349-363)detach(365-379)Sources/AblyChat/Room.swift (2)
attach(377-383)detach(386-392)
⏰ 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, iOS (Xcode 26.0)
- GitHub Check: Xcode, iOS (Xcode 26.0)
- GitHub Check: Xcode,
releaseconfiguration, macOS (Xcode 26.0) - GitHub Check: Xcode,
releaseconfiguration, iOS (Xcode 26.0) - GitHub Check: Xcode,
releaseconfiguration, tvOS (Xcode 26.0) - GitHub Check: Example app, macOS (Xcode 26.0)
- GitHub Check: Example app, tvOS (Xcode 26.0)
- GitHub Check: Xcode, macOS (Xcode 26.0)
- GitHub Check: Xcode, tvOS (Xcode 26.0)
- GitHub Check: SPM (Xcode 26.0)
- GitHub Check: SPM,
releaseconfiguration (Xcode 26.0)
🔇 Additional comments (20)
Tests/AblyChatTests/ChatAPITests.swift (1)
24-25: LGTM: assertion matches the new InternalError surface.Clear pattern-match against the expected case.
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)
84-86: LGTM: failure wraps ARTErrorInfo into InternalError correctly.No issues; mapping is precise and typed.
Sources/AblyChat/Errors.swift (1)
123-131: Review comment is incorrect; no changes needed.The review expects a global
extension Error { func toARTErrorInfo() }to exist, but the actual implementation is more type-safe:performAttachOperation()declaresasync throws(InternalError), so the caught error is already narrowed toInternalErrorat the catch site. ThetoARTErrorInfo()method exists only on theInternalErrorenum (Errors.swift:134), which is the correct approach. No global Error extension is needed or present, and this is not a deficiency—typed throws eliminate the need for it.Likely an incorrect or invalid review comment.
Sources/AblyChat/DefaultMessages.swift (2)
181-183: Correct: use InternalError with Result bridging.Resuming the continuation with .failure(InternalError.internallyThrown(.attachSerialIsNotDefined)) and later .get() adheres to the typed-throw guidance.
186-187: Good: standardized InternalError for attach failure cause.Centralizes channel failure under .channelFailedToAttach(cause:). Logging the error object is fine.
Sources/AblyChat/RoomLifecycleManager.swift (6)
19-22: Docs updated to reflect InternalError usage — looks right.The comments now describe InternalError throws per CHA‑RL9/PR3/PR10/PR6. No action required.
213-216: Discontinuity now emitted as ARTErrorInfo via InternalError — good.Keeps subscriber surface consistent while preserving original cause in userInfo.
395-401: Attach precondition errors correctly standardized.Throwing .roomIsReleasing / .roomIsReleased as InternalError matches RL1b/RL1c.
458-465: Detach precondition errors correctly standardized..roomIsReleasing, .roomIsReleased, and .roomInFailedState now unified under InternalError for RL2b/RL2c/RL2d.
612-613: Presence wait: invalid transition throws InternalError — good.Includes original ARTErrorInfo as cause, satisfying RL9c.
619-620: Presence wait: require attach standardized..presenceOperationRequiresRoomAttach(feature:) is the right InternalError here (PR3h/PR10h/PR6h).
Tests/AblyChatTests/InternalErrorTests.swift (1)
9-9: LGTM: use InternalError.fromAblyCocoa for passthrough.Confirms identity round‑trip to ARTErrorInfo.
Sources/AblyChat/DefaultMessageReactions.swift (1)
44-44: Verified: error mapping is correct.The
unableDeleteReactionWithoutNamecase correctly maps to.fixedStatusCode(.badRequest), which resolves to code 40000 and HTTP status 400, as required by CHA-MR11. The boundary conversion at line 44 properly throws the convertedARTErrorInfo, following the guideline to useInternalErrorinternally and convert at public API boundaries.Sources/AblyChat/Message.swift (3)
226-228: Verification confirmed: code 40000 and status 400 are correctly mapped.The error chain is properly implemented:
- ErrorCode.badRequest = 40000
- cannotApplyEventForDifferentMessage → .fixedStatusCode(.badRequest)
- InternalError→ARTErrorInfo conversion maintains code and status through the public API boundary
The implementation satisfies CHA-M11e (code 40000, status 400) as documented in Message.swift line 225.
252-254: LGTM: mismatched-serial now standardized via InternalError.Both code paths consistently use
InternalError.internallyThrown(.cannotApplyEventForDifferentMessage).toARTErrorInfo(), aligned with the spec: error code 40000 maps to "bad request" with HTTP status 400 (.badRequest). Line 227 (CHA-M11e, summary-event) and line 253 (CHA-M11b, message-event) are standardized correctly.
246-249: LGTM: created-event guard returns correct standardized ARTErrorInfo with error code 40000 and status code 400.Verification confirms that
cannotApplyCreatedMessageEventcorrectly maps toErrorCode.badRequest = 40000with HTTP status code400, satisfying spec requirement CHA-M11a.Tests/AblyChatTests/Helpers/Helpers.swift (4)
23-36: LGTM!The internally-thrown InternalError validation logic is clear and correct, with appropriate early returns for each validation check.
41-47: LGTM!The update from
.errorInfoto.fromAblyCocoacorrectly aligns with the new error wrapping structure introduced in this PR.
56-62: Note the specific nested pattern matching.The pattern
.internallyThrown(.other(.jsonValueDecodingError))at line 58 is quite specific with three levels of nesting. While this is appropriate for the current error structure, be aware that changes toInternalError's internal structure will require updating this pattern. Since this is in test helpers, the maintenance burden is acceptable.
66-72: LGTM!The new
isInternalErrorWithCasehelper is a clean and useful addition for test assertions against specific error cases.
|
Gonna merge this one — please let me know if you have any further comments @maratal |
This is a refactor that combines the
ChatErrorandInternalErrortypes (having three different error types —ARTErrorInfo,InternalError, andChatError— at play in the codebase was getting rather confusing!)This is groundwork for some further error-related changes.
Summary by CodeRabbit