-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ECO-4974] Throw ARTErrorInfo
from public API
#234
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis pull request refines error handling throughout the Chat SDK. It adds a new “Throwing errors” section to the documentation and updates numerous asynchronous method signatures to explicitly throw either an Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant API as Chat API Method
participant IC as InternalError Converter
participant PA as Public API Layer
C->>API: Call async method (e.g. sendMessage)
API->>API: Execute with async/continuation
alt An error occurs
API->>IC: Convert caught error → InternalError
IC-->>API: Return InternalError
API->>PA: Convert InternalError to ARTErrorInfo for public API
PA-->>C: Throw ARTErrorInfo
else Success
API-->>C: Return result
end
Possibly related PRs
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
c2cb928
to
dba13ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🔭 Outside diff range comments (1)
Sources/AblyChat/DefaultPresence.swift (1)
267-295
: 🛠️ Refactor suggestionRobust decoding of presence messages.
processPresenceGet
(lines 267-295) handles multiple possible missing fields (clientID, timestamp, data). The approach is well-structured. Consider grouping the repeated guard statements under a single descriptive error method to keep code DRY and to unify the error response, especially if more presence fields are introduced.
🧹 Nitpick comments (17)
CONTRIBUTING.md (1)
43-43
: Minor grammatical improvement neededThere's a small grammatical issue in this line.
- - It is not currently possible to create a `Task`, `CheckedContinuation`, or `AsyncThrowingStream` with a specific error type. You will need to instead return a `Result` and then call its `.get()` method. + - It is not currently possible to create a `Task`, `CheckedContinuation`, or `AsyncThrowingStream` with a specific error type. You will need to instead return a `Result` and then call its `.get()` method.🧰 Tools
🪛 LanguageTool
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return aResult
and then call its.get()
method. - `Dictionary.mapVal...(AI_HYDRA_LEO_CPT_ITS_IT)
Sources/AblyChat/Rooms.swift (1)
154-253
: Consistent internaldo throws(InternalError)
enclosure
By first catching errors asInternalError
and then converting toARTErrorInfo
only at the API boundary, you ensure more precise error handling logic inside the method. The stepwise approach (guarding existing room state vs. waiting for release) is well-structured, though it’s quite verbose. If the logic expands further, consider extracting subroutines to enhance readability.Sources/AblyChat/Room.swift (1)
232-232
: Suggest refined error code for clarity.
While wrapping anARTErrorInfo
into anInternalError
is valid, consider using a more descriptive or unique error code than40000
for greater clarity.Sources/AblyChat/PaginatedResult.swift (3)
10-12
: Consider converting these properties into async/throwing methods.
Definingnext
,first
, andcurrent
as computed properties that areasync throws
is unusual and may reduce clarity, since properties ordinarily imply trivial access. Converting them to methods (e.g.func next() async throws -> (any PaginatedResult<T>)?
) can make the asynchronous or error-prone nature more explicit.
25-30
: Clarify thenoErrorWithInvalidResponse
case.
Using.failure(PaginatedResultError.noErrorWithInvalidResponse.toInternalError())
is fine, but the name can be confusing. Consider naming it something likeinvalidResponse
if that is the true meaning.
45-46
: Use a common error type or expand this enum.
SincePaginatedResultError
currently contains a single case, you could either rename it to better reflect the scenario (e.g.InvalidResponseError
) or consolidate it into an existing error enum if appropriate.Sources/AblyChat/DefaultMessages.swift (2)
117-125
: Potential repeated throw in error handling path.
Inside the catch block (lines 117-125), you rethrow the same error after logging it. This is fine, but if you plan to unify error types, consider converting to a single typed error on first throw. Right now, some errors get converted toARTErrorInfo
while others pass through as-is.
160-190
: Repeated do/catch code for REST calls.
The methodsget
,send
,update
, anddelete
(lines 160-190) follow a repetitive pattern: a do block that callschatAPI.*
and throwserror.toARTErrorInfo()
. Consider factoring this pattern into a helper function to reduce code duplication.- do { - return try await chatAPI.getMessages(roomId: roomID, params: options) - } catch { - throw error.toARTErrorInfo() - } + return try await convertError { + try await chatAPI.getMessages(roomId: roomID, params: options) + }Sources/AblyChat/ChatAPI.swift (2)
22-26
: Consider using a safer conversion for numbers.
The initializer forSendMessageResponse
(lines 22-25) forcibly convertscreatedAt
usingjsonObject.numberValueForKey("createdAt")
. If the server returns a floating value or an unexpected numeric type, it might cause runtime issues. Consider safe-casting or providing a fallback.
134-170
: Delete message request with partial body.
deleteMessage
(lines 134-170) conditionally addsdescription
andmetadata
to the request body. If a future logic introduces additional optional fields, consider centralizing the body-building logic in a single function. This helps reduce code duplication across message operations.Sources/AblyChat/RoomLifecycleManager.swift (4)
590-600
: Avoid indefinite continuation in OperationResultContinuations.
While storing continuations by operation ID is straightforward, carefully handle any edge cases where an operation might complete exceptionally or never complete. If a continuation is never removed, it can lead to memory leaks or indefinite hangs.
655-656
: Recommend structured concurrency for waiting logic.
waitForCompletionOfOperationWithID
uses an ad-hoc continuation approach. While valid, consider adopting Swift’s new concurrency patterns (e.g.,AsyncStream
) for operation coordination. This can simplify code and reduce the risk of continuation misuse.
865-897
: Check for stale references upon detach.
InperformDetachOperation
, if the manager is deallocated or the contributor references become stale mid-cycle, you might end up with a partial operation. You do handle repeated detach attempts for non-failed contributors, but ensure no concurrency drift breaks the final state.
1220-1252
: Wait logic for presence operations can be canceled.
waitToBeAbleToPerformPresenceOperations
(lines 1220-1252) uses subscription to wait for status changes. If the task is canceled, the next status change might never be handled. Consider adding a short-circuit orTask.isCancelled
check in your loop to avoid waiting indefinitely in canceled tasks.Sources/AblyChat/DefaultPresence.swift (3)
72-97
: isUserPresent concurrency approach.
isUserPresent
lines 72-97 also callswaitToBeAbleToPerformPresenceOperations
before callingchannel.presence.getAsync(...)
. This is correct, but be sure your concurrency usage doesn’t cause race conditions if presence changes while you’re waiting.
167-199
: Leaving presence with typed throws.
leave(optionalData:)
(lines 167-199) replicates the same approach asenter
. Code duplication is minimal but might be further simplified if you wrap the logic in a shared helper since it’s nearly identical—only the presence action differs.
252-257
: Add more descriptive error message.
WhenpresenceData
isnil
at line 254, you throw a generic "Received incoming message without data". Improve clarity by specifying that the presence data was unexpectedly nil to help debugging.- let error = ARTErrorInfo.create(withCode: 50000, status: 500, message: "Received incoming message without data") + let error = ARTErrorInfo.create(withCode: 50000, status: 500, message: "Received nil presence data in message")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (42)
CONTRIBUTING.md
(1 hunks)Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift
(1 hunks)Sources/AblyChat/ChatAPI.swift
(7 hunks)Sources/AblyChat/DefaultMessages.swift
(7 hunks)Sources/AblyChat/DefaultOccupancy.swift
(1 hunks)Sources/AblyChat/DefaultPresence.swift
(5 hunks)Sources/AblyChat/DefaultRoomLifecycleContributor.swift
(1 hunks)Sources/AblyChat/DefaultRoomReactions.swift
(1 hunks)Sources/AblyChat/DefaultTyping.swift
(3 hunks)Sources/AblyChat/Errors.swift
(4 hunks)Sources/AblyChat/Extensions/Dictionary+Extensions.swift
(1 hunks)Sources/AblyChat/Headers.swift
(3 hunks)Sources/AblyChat/InternalError.swift
(1 hunks)Sources/AblyChat/JSONCodable.swift
(22 hunks)Sources/AblyChat/Message.swift
(2 hunks)Sources/AblyChat/Messages.swift
(6 hunks)Sources/AblyChat/Occupancy.swift
(2 hunks)Sources/AblyChat/PaginatedResult.swift
(3 hunks)Sources/AblyChat/Presence.swift
(7 hunks)Sources/AblyChat/PresenceDataDTO.swift
(1 hunks)Sources/AblyChat/Room.swift
(4 hunks)Sources/AblyChat/RoomFeature.swift
(2 hunks)Sources/AblyChat/RoomLifecycleManager.swift
(17 hunks)Sources/AblyChat/RoomReactionDTO.swift
(2 hunks)Sources/AblyChat/RoomReactions.swift
(1 hunks)Sources/AblyChat/Rooms.swift
(6 hunks)Sources/AblyChat/Typing.swift
(2 hunks)Tests/AblyChatTests/ChatAPITests.swift
(1 hunks)Tests/AblyChatTests/DefaultMessagesTests.swift
(3 hunks)Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
(1 hunks)Tests/AblyChatTests/DefaultRoomPresenceTests.swift
(6 hunks)Tests/AblyChatTests/DefaultRoomTypingTests.swift
(2 hunks)Tests/AblyChatTests/DefaultRoomsTests.swift
(2 hunks)Tests/AblyChatTests/Helpers/Helpers.swift
(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoom.swift
(2 hunks)Tests/AblyChatTests/Mocks/MockRoomFactory.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
(2 hunks)Tests/AblyChatTests/PresenceDataDTOTests.swift
(1 hunks)Tests/AblyChatTests/RoomReactionDTOTests.swift
(2 hunks)
🧰 Additional context used
🪛 LanguageTool
CONTRIBUTING.md
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return a Result
and then call its .get()
method. - `Dictionary.mapVal...
(AI_HYDRA_LEO_CPT_ITS_IT)
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like: do throws(InternalError) { … }
. - The compiler will never infer the t...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g. let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: Example app, tvOS (Xcode 16)
- GitHub Check: Example app, iOS (Xcode 16)
- GitHub Check: Example app, macOS (Xcode 16)
- GitHub Check: Xcode, tvOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, tvOS (Xcode 16) - GitHub Check: Xcode, iOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, iOS (Xcode 16) - GitHub Check: Xcode, macOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, macOS (Xcode 16) - GitHub Check: SPM (Xcode 16)
🔇 Additional comments (113)
Sources/AblyChat/PresenceDataDTO.swift (1)
13-13
: Appropriate removal of throws keywordThe removal of the
throws
keyword from this initializer simplifies the API since this simple assignment operation doesn't need error propagation. This aligns with the broader effort to standardize error handling across the codebase.Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift (1)
2194-2194
: Appropriately broadened error type to accommodate new error handlingChanging the error type from
ARTErrorInfo?
toError?
correctly adapts the test to the updated error handling mechanism. This allows the test to catch any error type thrown by the implementation, which is necessary since the SDK is standardizing on different error types for different layers.Tests/AblyChatTests/ChatAPITests.swift (1)
23-27
: Correctly updated error checking logicThe modification to use pattern matching with
InternalError
correctly adapts the test to the new error handling mechanism. This update properly verifies that errors are now wrapped in theInternalError
type, which is consistent with the SDK's standardized approach to error handling.Sources/AblyChat/RoomReactions.swift (1)
17-17
: Improved API clarity with typed throwsThis change implements the PR objective by specifying that the method throws
ARTErrorInfo
rather than a generic error. This provides better API clarity for users, allowing them to make more informed decisions about error handling, particularly for determining whether operations can be retried based on the error's status code.Tests/AblyChatTests/PresenceDataDTOTests.swift (1)
21-25
: Improved error validation pattern.The test now uses a closure pattern to capture and validate the error type using
isInternalErrorWithCase
, which is more flexible than directly checking for a specific error type. This aligns with the PR's objective to standardize on typed error handling across the SDK.Sources/AblyChat/DefaultRoomReactions.swift (1)
24-24
: Function signature updated to use typed throws.The method now explicitly specifies that it throws
ARTErrorInfo
instead of a generic error, which aligns with the PR objective to standardize error handling in the public API. This change improves API clarity by communicating the exact error type that callers should expect to handle.Sources/AblyChat/Headers.swift (3)
1-2
: Added Ably import.Added import for the Ably module, which is likely needed for the error handling modifications in this file.
78-78
: Updated initializer to use typed throws.The initializer now explicitly declares that it throws
InternalError
instead of a generic error, improving type safety and API clarity.
89-89
: Modified error transformation pattern.The error is now converted to an
InternalError
using thetoInternalError()
extension method before being thrown. This ensures consistency with the updated error handling approach across the SDK.Sources/AblyChat/Errors.swift (4)
215-215
: Added new case for handling internal errors without ARTErrorInfo.The new
nonErrorInfoInternalError
case allows for representing internal errors that don't have an associatedARTErrorInfo
, which enhances the error handling system's flexibility.
228-231
: Added error status code handling for internal errors.Internal errors without an
ARTErrorInfo
are now treated as non-recoverable user errors with a.badRequest
status code. This provides a consistent approach to handling these errors in the public API.
314-317
: Added localized description for internal errors.The implementation provides a simple string representation of the internal error enum case. This ensures that all errors have descriptive messages for debugging and user feedback.
347-348
: Updated error cases without a cause.Added the new
nonErrorInfoInternalError
case to the list of errors that don't have an underlying cause, maintaining consistency in the error handling implementation.Sources/AblyChat/Extensions/Dictionary+Extensions.swift (1)
1-8
: Good implementation of typed error handling for dictionary transformations.This new utility method
ablyChat_mapValuesWithTypedThrow
properly preserves the error type thrown by the transform function, allowing for more precise error handling compared toDictionary.mapValues
which usesrethrows
.The implementation correctly preserves key uniqueness and propagates the typed error, which aligns with the PR objective of standardizing error types throughout the API.
Tests/AblyChatTests/DefaultRoomTypingTests.swift (2)
83-84
: Good use of typed throws syntax for error handling.Changed the
do
block to usedo throws(ARTErrorInfo)
to explicitly specify the expected error type. This change removes the need for explicit casting of the error toARTErrorInfo
later in the catch block, making the code cleaner while maintaining type safety.This aligns well with the PR objective of standardizing error types across the codebase.
108-109
: Consistent application of typed throws pattern.Similar to the previous change, this modification uses
do throws(ARTErrorInfo)
to specify the expected error type, ensuring consistent error handling across test methods.This approach follows the typed throws pattern being applied throughout the codebase.
Tests/AblyChatTests/Mocks/MockRoomFactory.swift (1)
16-16
: Updated method signature with typed throws to specifyInternalError
.Modified the method signature to use
throws(InternalError)
instead of genericthrows
, which makes the error type explicit. This change supports the PR objective of standardizing error types throughout the codebase.The implementation correctly specifies that the
createRoom
method can throw anInternalError
, providing more precise error handling information to callers.Tests/AblyChatTests/RoomReactionDTOTests.swift (3)
10-14
: Good update to use structured error handling withisInternalErrorWithCase
The updated test now properly checks that the error is an
InternalError
with the specific case of.jsonValueDecodingError
, aligning with the changes made to standardize error types in the library.
19-23
: Consistent error verification approachThis follows the same pattern of verifying the specific
InternalError
case, maintaining consistency with the other test updates.
73-77
: Aligned error handling pattern for Extras testsThe test for
RoomReactionDTO.Extras
now follows the same structured error verification pattern, creating consistency across all test cases.Tests/AblyChatTests/InternalErrorTests.swift (1)
1-25
: Well-structured tests for error conversionThese tests properly validate the two key conversion paths for
InternalError
toARTErrorInfo
:
- When the underlying error is already an
ARTErrorInfo
, it should be returned directly- When the underlying error is not an
ARTErrorInfo
, it should be properly wrappedThis implementation directly supports the PR objective of standardizing public API errors as
ARTErrorInfo
and ensures the conversion mechanism works correctly.Tests/AblyChatTests/DefaultRoomPresenceTests.swift (2)
106-114
: Updated to use typed throws in error handlingThe test now explicitly declares the expected error type with
throws(ARTErrorInfo)
, which aligns with the changes to standardize on typed throws in the public API.
131-138
: Consistent error type specification across testsAll error handling blocks have been updated to use
throws(ARTErrorInfo)
, ensuring consistency across the test suite and properly validating that the functions throw the expected error type.Also applies to: 224-232, 249-256, 329-336, 399-407
Tests/AblyChatTests/Mocks/MockRoom.swift (2)
1-1
: Added necessary import for ARTErrorInfoAdded the Ably import which is required for the ARTErrorInfo type used in the method signatures.
47-47
: Updated method signatures to use typed throwsThe
attach()
anddetach()
methods now explicitly specify that they throwARTErrorInfo
, aligning with the PR goal of standardizing on typed throws in the public API.Also applies to: 51-51
Sources/AblyChat/DefaultOccupancy.swift (1)
56-62
: Method correctly updated to throw ARTErrorInfoThe
get()
method has been properly updated to explicitly throwARTErrorInfo
instead of a generic error, aligning with the PR's objective of standardizing error handling. The implementation effectively wraps the original code in a do-catch block and converts any caught errors toARTErrorInfo
using thetoARTErrorInfo()
method.This change ensures that the error type is consistent with the updated protocol definition and allows clients to access additional error information like
statusCode
.Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift (2)
67-78
: Properly updatedattach()
method to throw InternalErrorThe method signature has been correctly updated to throw
InternalError
instead of a generic error. The implementation now properly wraps the call toperformBehavior()
in a do-catch block and converts any caught errors toInternalError
using thetoInternalError()
method.This change maintains consistency with the rest of the codebase's error handling approach.
82-93
: Properly updateddetach()
method to throw InternalErrorSimilar to the
attach()
method, thedetach()
method has been correctly updated to throwInternalError
and includes proper error conversion in the do-catch block.This change is consistent with the error handling approach being implemented across the codebase.
Sources/AblyChat/Occupancy.swift (2)
30-30
: Protocol method signature correctly updatedThe
get()
method signature in theOccupancy
protocol has been properly updated to explicitly throwARTErrorInfo
. This change aligns with the PR objective of standardizing public API error types and matches the implementation inDefaultOccupancy
.This change ensures that clients will have access to the error's
statusCode
to help determine if they can retry actions that resulted in errors.
69-69
: JSONObjectDecodable initializer correctly updatedThe initializer signature has been properly updated to throw
InternalError
instead of a generic error. This change is consistent with the pattern of having internal methods throwInternalError
while public API methods throwARTErrorInfo
.This change contributes to the standardization of error types throughout the codebase.
Tests/AblyChatTests/Mocks/MockFeatureChannel.swift (1)
25-34
: Mock implementation correctly updated to throw InternalErrorThe
waitToBeAbleToPerformPresenceOperations
method has been properly updated to throwInternalError
instead ofARTErrorInfo
. The implementation now includes a do-catch block that correctly converts any caught errors toInternalError
using thetoInternalError()
method.This change maintains consistency with the rest of the error handling approach in the codebase and properly reflects the behavior of the interface it's implementing.
CONTRIBUTING.md (2)
37-46
: Great addition of error handling guidelinesThe documentation on typed throws is clear and comprehensive, offering valuable guidance on when to use
ARTErrorInfo
versusInternalError
. The guidelines align well with the PR objective of ensuring consistent error typing.🧰 Tools
🪛 LanguageTool
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return aResult
and then call its.get()
method. - `Dictionary.mapVal...(AI_HYDRA_LEO_CPT_ITS_IT)
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
45-46
: Documentation clearly explains Swift typing inference limitationsThe explanation of error type inference limitations in
do
blocks and closures is excellent. It will help developers understand why they need to explicitly specify error types in certain scenarios.🧰 Tools
🪛 LanguageTool
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
Sources/AblyChat/DefaultRoomLifecycleContributor.swift (2)
37-39
: Updated error type aligns with internal error handling strategyChanging from
throws(ARTErrorInfo)
tothrows(InternalError)
correctly implements the internal error handling strategy outlined in the contributing guidelines, whereInternalError
is used internally and converted toARTErrorInfo
at the public API boundary.
41-43
: Updated error type aligns with internal error handling strategySimilarly, updating the detach method to throw
InternalError
maintains consistency with the internal error handling approach.Tests/AblyChatTests/Helpers/Helpers.swift (4)
5-18
: Improved error checking logic for test helpersThe updated implementation correctly checks for both direct
ARTErrorInfo
errors andInternalError
instances that wrapARTErrorInfo
. This enhancement aligns with the new error handling strategy and improves test reliability.
35-41
: Helpful utility function for internal error testingThis new helper function provides a clean way to verify when an
InternalError
is wrapping a specificARTErrorInfo
, which will be useful for testing the error conversion logic mentioned in the contributing guidelines.
43-66
: Well-structured error case enumerationThe addition of the
Case
enum provides a clear categorization of internal error types, making tests more readable and maintainable. TheenumCase
property offers a convenient way to check error types without complex pattern matching.
68-74
: Useful utility for case-based error testingThis function complements the enumeration by providing a simple way to check if an error is of a specific case type, which will streamline test assertions.
Sources/AblyChat/Typing.swift (3)
30-30
: Public API now uses typed throws with ARTErrorInfoThe change to
throws(ARTErrorInfo)
correctly implements the requirements specified in the contributing guidelines, ensuring that the public API uses typed throws withARTErrorInfo
as the error type.
42-42
: Public API now uses typed throws with ARTErrorInfoThe
start()
method now correctly specifiesARTErrorInfo
as its thrown error type, aligning with the PR objective.
50-50
: Public API now uses typed throws with ARTErrorInfoThe
stop()
method now correctly specifiesARTErrorInfo
as its thrown error type, aligning with the PR objective.Sources/AblyChat/RoomReactionDTO.swift (2)
43-43
: Good use of typed throwsThe initializer now explicitly declares that it throws
InternalError
, which aligns with the PR objective to make error handling more specific and consistent across the codebase.
61-64
: Improved error handling with typed throwsThe implementation now uses
ablyChat_mapValuesWithTypedThrow
to explicitly handle potential errors with theInternalError
type during JSON parsing. This is a good pattern that increases type safety and makes error handling more predictable.Tests/AblyChatTests/DefaultMessagesTests.swift (3)
61-67
: Temporary workaround for compiler crashThe TODO comment explains that this is a temporary solution to work around a compiler crash. Make sure to track issue #233 so this code can be reverted once Xcode 16.3 is released.
Could you confirm if there's a plan to monitor the release of Xcode 16.3 so these workarounds can be removed promptly?
273-277
: Improved error validation approachThe test now uses a more specific validation for the thrown error, checking if it's an
InternalError
wrapping anARTErrorInfo
. This aligns with the PR objective to ensure proper error types are thrown from the API.
318-324
:✅ Verification successful
Same compiler crash workaround
This is the same workaround pattern as seen earlier in the file. The implementation maintains the test's intent while working around the compiler limitation.
🏁 Script executed:
#!/bin/bash # Check if there are other instances of this workaround pattern that might need to be reverted later rg --type swift "TODO: avoids compiler crash.*revert once Xcode 16\.3 released" -B 1 -A 5Length of output: 2579
Compiler Crash Workaround Validated Across Test Files
The workaround pattern in the tests (as seen in both DefaultMessagesTests.swift and DefaultRoomsTests.swift) consistently applies the necessary temporary fix for the Xcode compiler crash. The tests preserve their intended behavior, and the pattern is uniformly applied. Please ensure that when Xcode 16.3 is released, these workarounds are reverted as noted in the TODO comments.
Sources/AblyChat/RoomFeature.swift (2)
73-73
: Implementation correctly updatedThe implementation in
DefaultFeatureChannel
has been properly updated to match the protocol change, maintaining consistency in error handling.
61-61
:✅ Verification successful
Protocol method now throws InternalError
The method signature has been updated to throw
InternalError
instead ofARTErrorInfo
. This aligns with the PR objective but make sure all implementers of this protocol have been updated accordingly.
🏁 Script executed:
#!/bin/bash # Check for implementations of waitToBeAbleToPerformPresenceOperations to ensure they've been updated rg --type swift "func waitToBeAbleToPerformPresenceOperations.*\(" -A 1Length of output: 2928
Verified: Protocol Method Signature Update Confirmed
The change to throw
InternalError
instead ofARTErrorInfo
has been successfully propagated. All relevant implementations (including those in Sources and Tests) are updated accordingly:
- Sources/AblyChat/RoomFeature.swift – Signature now correctly uses
InternalError
.- Sources/AblyChat/RoomLifecycleManager.swift – Updated signature in both protocol and its internal implementation.
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift and MockFeatureChannel.swift – Mocks now reflect the updated method signature.
- Additional tests in DefaultRoomLifecycleManagerTests.swift confirm consistent usage across scenarios.
No further changes are required.
Sources/AblyChat/Message.swift (3)
155-155
: Good use of typed throwsThe initializer now explicitly declares that it throws
InternalError
, which aligns with the PR objective to make error handling more specific.
165-167
: Improved error handling with typed throwsThe implementation now uses
ablyChat_mapValuesWithTypedThrow
to handle errors with the specificInternalError
type when parsing JSON values. This improves type safety and error handling predictability.
170-170
: Consistent typed throws in operation mappingThe operation mapping closure now explicitly throws
InternalError
, maintaining consistency with the other error handling improvements in this file.Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift (2)
19-29
: Good use of typed throws inperformAttachOperation
The typed throws withInternalError
cleanly encapsulate internal Ably errors (ARTErrorInfo
). The logic for rethrowing viaerror.toInternalError()
aligns well with the new error handling approach.
31-41
: ConsistentInternalError
usage inperformDetachOperation
This method follows the same pattern asperformAttachOperation
, ensuring uniform error propagation.Sources/AblyChat/InternalError.swift (3)
1-16
: Well-structured definition ofInternalError
Defining a dedicated internal error enum reduces ambiguity and provides a clear mapping fromARTErrorInfo
to internal and public-facing errors.
17-31
:toARTErrorInfo()
andmessage
property provide smooth error translation
These functions uniformly translate internal errors intoARTErrorInfo
and facilitate consistent logging. Good clarity in bridging internal and external error layers.
33-61
: Extensions streamline conversions toInternalError
Each extension method clearly wraps domain-specific error types for unified handling. This modular approach helps maintain consistency as the SDK evolves.Sources/AblyChat/Rooms.swift (5)
28-28
: Typed throws onget(...): async throws(ARTErrorInfo)
ExposingARTErrorInfo
at the public API boundary meets the PR objective to consistently throw typed Ably errors.
256-260
: Clear final conversion toARTErrorInfo
CatchingInternalError
and rethrowing asARTErrorInfo
satisfies the typed error requirement. Good job ensuring that bridging logic is at the end.
263-283
: Helper functions for creation-failure signaling
ThemakeCreationFailureFunctions
approach is creative, providing structured ways to inject errors into in-flight async tasks. This design is flexible, though it’s somewhat intricate. Consider adding unit tests that ensure all code paths for success/failure are covered.Would you like a sample test snippet or script to verify coverage of these code paths?
294-299
:createRoom(...)
modernization
Shifting toasync throws(InternalError)
is consistent with the rest of the refactor. The method body is straightforward and easy to follow.
335-335
: Graceful logging for release-induced failure
Transforming the “room released” scenario into anInternalError
clarifies the reason for creation failure. Adequate debug logs help with diagnosis.Sources/AblyChat/Room.swift (7)
91-91
: Public API typed throws alignment looks good.
The newfunc attach() async throws(ARTErrorInfo)
signature aligns well with the PR objective of exposingARTErrorInfo
from the public API.
98-98
: Consistent typed throws for detaching.
Thefunc detach() async throws(ARTErrorInfo)
signature is consistent with the typed-throw approach used inattach()
.
142-142
: Internal error type usage is acceptable.
UsingInternalError
increateRoom
at an internal protocol level is fine, as these errors are not directly exposed through the public interface.
148-148
: Continuation of internal error typing.
This factory method’sasync throws(InternalError)
signature is consistent with the rest of the internal error handling approach.
224-224
: Initialization error handling.
The constructor’sasync throws(InternalError)
signature cleanly distinguishes internal errors duringDefaultRoom
creation.
397-402
: Rethrowing asARTErrorInfo
is correct.
Thisattach()
implementation properly catches lower-level errors and rethrows them asARTErrorInfo
, matching the public API contract.
405-410
: Consistent detach code.
Similar toattach()
, thedetach()
method correctly wraps and rethrows errors asARTErrorInfo
.Sources/AblyChat/Messages.swift (7)
18-18
: Typed throws forsubscribe(bufferingPolicy:)
.
Changing the signature toasync throws(ARTErrorInfo)
is consistent with the new approach of exposingARTErrorInfo
at the public API level.
23-23
: Additive typed throws in defaultsubscribe()
.
ExposingARTErrorInfo
here as well ensures consistency with the buffering-policy overload.
33-33
: Query method error type.
Enablingget(options:)
to throwARTErrorInfo
clarifies error details for message history retrieval.
47-47
: Enhanced error detail for sending messages.
UsingARTErrorInfo
insend(params:)
improves transparency around send-failure causes.
63-63
: Specific typed throw for message updates.
update(newMessage:description:metadata:)
now explicitly throwingARTErrorInfo
matches the broader error-handling improvements.
78-78
: Explicit error type for message deletion.
delete(message:params:)
adoptingARTErrorInfo
consistently completes the suite of typed throws for message operations.
89-89
: Unified subscription convenience.
The defaultsubscribe()
method’sasync throws(ARTErrorInfo)
complements the buffering-policy-based overload cleanly.Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift (9)
6-29
: Bridge from callback-based requests to Swift concurrency.
The internalrequestAsync
method elegantly wraps the callback inwithCheckedContinuation
, properly rethrowing errors asInternalError
.
32-46
: Asynchronous attach extension.
attachAsync()
correctly maps the callback-based attach flow to async/await, rethrowingARTErrorInfo
asInternalError
for internal usage.
48-62
: Asynchronous detach extension.
detachAsync()
follows the same pattern asattachAsync()
, ensuring consistent error wrapping.
65-72
: Concurrent-safe presence message struct.
DefiningPresenceMessage
separately with only required fields is a clean, sendable-friendly approach.
74-85
: Initializer bridging toPresenceMessage
.
Reading fields fromARTPresenceMessage
and converting them toPresenceMessage
is straightforward and correct.
88-105
: Fetching presence members asynchronously.
getAsync()
properly wraps presence retrieval in an async/await continuation, then rethrows asInternalError
.
107-123
: Query-based presence fetch.
Similarly,getAsync(_ query:)
is well-implemented, parallelinggetAsync()
to retrieve presence with typed error handling.
125-139
: Asynchronous presence leave.
leaveAsync(_:)
bridging from callbacks to async is consistent, ensuring typedInternalError
from underlyingARTErrorInfo
.
141-155
: Client-specific presence entry.
enterClientAsync
parallelsleaveAsync
, maintaining consistency when bridging the callback flow.Sources/AblyChat/PaginatedResult.swift (3)
19-19
: Good use of typedResult
with continuations.
Switching toCheckedContinuation<Result<PaginatedResultWrapper<Response>, InternalError>, Never>
clarifies success/failure scenarios and aligns with typed error handling.
98-100
: LGTM: Extension for conversion is well-structured.
The helper methodtoPaginatedResult(items:)
cleanly encapsulates logic for converting low-level responses into a typed wrapper.
36-36
:✅ Verification successful
Validate Swift language version for advanced closure throws syntax.
try jsonValues.map { jsonValue throws(InternalError) in … }
is valid in newer Swift versions; confirm that your minimum deployment environment supports this feature. Otherwise, you may need to map errors within a standarddo … catch
block.
🏁 Script executed:
#!/bin/bash # Searching for Swift language version constraints in the project files rg -A 3 "SWIFT_VERSION|swift-tools-version"Length of output: 1770
Swift Language Version Confirmed
The project is correctly configured to use Swift 6.0 (as seen in both
Package.swift
and the Xcode project settings). This version supports the advanced closurethrows(InternalError)
syntax, so no changes are required.
- Confirmed in
Package.swift
:// swift-tools-version: 6.0
- Confirmed in
Example/AblyChatExample.xcodeproj
:SWIFT_VERSION = 6.0
Sources/AblyChat/DefaultTyping.swift (3)
122-161
: Typed error conversion boundary is consistent.
WrappingInternalError
within ado throws(InternalError)
block and converting it toARTErrorInfo
externally ensures the public API consistently exposesARTErrorInfo
. This design is aligned well with typed error handling.
164-191
: Stop method concurrency approach looks consistent.
The concurrency approach forstop()
uses the same pattern asstart()
. No major concerns unless multiple actors callstop()
simultaneously, which your concurrency strategy may already handle.
198-210
: Good approach to refiningprocessPresenceGet
.
This helper ensures each member has aclientId
. Throwing anInternalError
as soon as one is missing is a clean fail-fast mechanism.Sources/AblyChat/Presence.swift (1)
15-15
: Consistent usage ofARTErrorInfo
in public methods.
All these methods now throwARTErrorInfo
, which aligns with the newly standardized typed throws approach. This improves error specificity and consistency throughout the presence API.Also applies to: 27-27, 39-39, 49-49, 59-59, 69-69, 99-99, 107-107, 115-115
Sources/AblyChat/DefaultMessages.swift (3)
261-280
: Clarify distinction between InternalError vs. ARTErrorInfo.
InresolveSubscriptionStart(...)
(line 261) andserialOnChannelAttach(...)
(line 280), you switch from throwingInternalError
toARTErrorInfo
in some places. This is consistent with the rest of the typed-throws approach, but ensure all code paths use the final error type the caller expects. Thoroughly test edge cases (e.g., missingchannelSerial
) to confirm.
295-335
: Check concurrency safety of withCheckedContinuation usage.
ThewithCheckedContinuation
call (lines 295-335) properly resumes the continuation only once. However, if multiple channel events arrive in quick succession, ensure there's no race that might resume or mutatenillableContinuation
prematurely. Consider usingactor-isolated
orasync
streams to handle potential concurrency complexities.
60-69
:✅ Verification successful
Handle concurrency within Task blocks carefully.
When subscribing to realtime messages (lines 60-69), each message callback spawns aTask
that performs multiple checks and can throwARTErrorInfo
. This is correct usage of Swift concurrency, but be mindful that ifself
is deallocated or ifchannel
is detached mid-callback, subsequent calls (likemessage.extras
) could fail. Consider adding defensive checks if needed.Do you want to verify if the object remains valid throughout the entire
Task
lifecycle? You could run a quick script to grep for possible mid-callback references:
🏁 Script executed:
#!/bin/bash rg -A 5 "Task {"Length of output: 94
Ensure Task Block’s Object Validity Remains Intact
In the realtime message subscription (lines 60–69 ofSources/AblyChat/DefaultMessages.swift
), the usage ofTask { … }
follows Swift concurrency patterns and is acceptable. However, please verify that any objects referenced within the Task’s closure—such asself
orchannel
—are still valid for the entire Task lifecycle. This is important because if, for example,self
is deallocated or the channel is detached before the Task completes, subsequent property accesses (likemessage.extras
, if they occur) might fail. Consider adding defensive checks (or adjusting capture semantics, such as using[weak self]
) if these objects’ lifetimes are uncertain.
🏁 Script executed:
#!/bin/bash # Use fixed-string search to avoid regex errors when looking for "Task {" in the file rg -F -A 5 "Task {" Sources/AblyChat/DefaultMessages.swiftLength of output: 1284
Concurrency Handling Validated: No Further Changes Needed
The verification shows that the Task blocks in
Sources/AblyChat/DefaultMessages.swift
are using Swift concurrency correctly. In the realtime message subscription block (lines 60–69), there isn’t an unnecessary capture ofself
—and whereself
is needed (such as within the MainActor block or during attachment handling), the code already leverages[weak self]
or optional chaining. This defensive approach adequately prevents issues due to object deallocation or detached channels during the Task’s lifecycle.
- Realtime message subscription Task: Does not capture
self
unnecessarily.- Subsequent Task blocks: Correctly use
[weak self]
and safe optional chaining when referencing instance members.- General recommendation: Continue monitoring if new references are added mid-callback, but no changes are required at this time.
Sources/AblyChat/ChatAPI.swift (3)
13-16
: Ensure paginated request usage is correct.
getMessages
(lines 13-16) now returns a typedany PaginatedResult<Message>
withthrows(InternalError)
. Verify all call sites handleInternalError
properly and confirm that your unwrapped JSON decoding doesn’t miss potential edge cases.
43-78
: Consistent error transformation for ‘sendMessage’.
sendMessage
now throwsInternalError
ifclientId
is nil or a network error occurs. This is good for typed error consistency. Just ensure that the top-level code usingsendMessage
can handleInternalError
uniformly. No issues found with concurrency.
172-205
: makePaginatedRequest concurrency check.
makePaginatedRequest
(lines 195-205) usesrequestAsync
internally. This approach looks sound. Verify concurrency usage, especially if other tasks cancel or alter therealtime
reference. Ensure therealtime.requestAsync
call doesn’t get canceled mid-pipeline.Sources/AblyChat/RoomLifecycleManager.swift (4)
44-50
: Typed errors in lifecycle operations.
performAttachOperation
andperformDetachOperation
now throwInternalError
. This ensures alignment with typed error handling. Double-check that calling code properly awaits or handles these operations, as they can fail at runtime.
746-759
: Handle potential race conditions around status changes.
InperformAttachOperation
(lines 746-759), if the channel state changes again while you wait, ensure you don’t incorrectly override or skip states. You do partial checks withstatus.operationID
. Just confirm that concurrency or reentrancy can’t cause you to lose an important update.
790-817
: Graceful fallback for suspended or failed states.
InsideperformAttachmentCycle
, suspended or failed states cause immediate exit from the loop with subsequent scheduling of a retry or rundown operation (lines 805-817). This design makes sense. Validate that any ephemeral state changes incontributorState
after the error are safe to ignore.
971-972
: Retain original error context when rethrowing.
After the detach loop, an error is rethrown at lines 971-972 usingfirstDetachError.toInternalError()
. That preserves typed error consistency. Confirm the original error’s message and cause remain visible to end users.Sources/AblyChat/DefaultPresence.swift (4)
22-45
: Unified typed throws for ‘get()’.
Yourget()
function lines 22-45 now usesthrows(ARTErrorInfo)
externally while converting internal errors toInternalError
first. This is consistent but watch for any new internal error types that never get converted toARTErrorInfo
, e.g.,PresenceDataDTO
decoding errors.
107-130
: Converting presence operation errors.
Forenter(optionalData:)
(lines 107-130), you correctly catch any errors and rethrow them via.toARTErrorInfo()
. This matches the typed throw approach. No immediate concerns, but continued coverage in tests will help ensure it’s robust.
142-165
: Updating presence with fallback data.
Inupdate(optionalData:)
(lines 142-165), you build aPresenceDataDTO
and then callpresence.updateAsync
. Ifdata
is nil, you pass an empty object. Confirm with product owners whether passing no data or an empty object is the intended default.
297-321
: Prevent partial data in presence subscription events.
processPresenceSubscribe
(lines 297-321) again decodes presence data. IfclientId
ortimestamp
is missing, you throw an error. This is consistent. Ensure that your documentation clarifies possible behaviors to library consumers if partial presence data arrives from unexpected sources.Sources/AblyChat/JSONCodable.swift (7)
9-9
: Good update to the protocol method signaturesChanging the
init(jsonValue:)
method to explicitly throwInternalError
instead of a generic error aligns well with the PR objective of standardizing error types. This provides more type safety and clearer error contracts.
26-26
: Consistent error type specificationThe update to
init(jsonObject:)
to throwInternalError
specifically maintains consistency with other method signatures in this file. This is a good practice for API clarity.
38-41
: Good error handling implementationConverting
JSONValueDecodingError
toInternalError
using thetoInternalError()
method ensures consistent error types are propagated through the public API. This aligns with the PR objectives to standardize error handling.
58-68
: Appropriate method signature updateThe update to the
objectValueForKey
method signature ensures consistent error handling. The implementation correctly usestoInternalError()
to convert domain-specific errors to the standardInternalError
type.
73-87
: Consistent error handling in optional methodsThe error handling in
optionalObjectValueForKey
has been appropriately updated. The method now explicitly throwsInternalError
and usestoInternalError()
for error conversion, maintaining consistency with the non-optional variant.
275-279
: Good error handling for RawRepresentable valuesThe
RawRepresentable
handling methods have been properly updated to throwInternalError
with consistent error conversion. This includes both the public methods and the private implementation method, which is thorough and maintains consistency.Also applies to: 286-292, 294-300
1-301
: Overall consistent implementation of the error handling strategyAll method signatures and error handling code have been consistently updated to use
InternalError
instead of generic errors. This systematic approach ensures a uniform error handling experience across the API, which aligns perfectly with the PR objectives of standardizing errors for better usability.The changes also enable users to access specific error information like
statusCode
(as mentioned in the PR objectives) by propagating the more specific error type. This is a significant improvement for API consumers who need to make informed decisions about error recovery.
dba13ae
to
1dfa781
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (14)
Tests/AblyChatTests/InternalErrorTests.swift (1)
16-16
: Consider consistent naming pattern for test methodsThe first test method is named
toARTErrorInfo_whenUnderlyingErrorIsARTErrorInfo
, while this one has atest
prefix:testToARTErrorInfo_whenUnderlyingErrorIsNotARTErrorInfo
. For consistency, consider removing thetest
prefix.- func testToARTErrorInfo_whenUnderlyingErrorIsNotARTErrorInfo() { + func toARTErrorInfo_whenUnderlyingErrorIsNotARTErrorInfo() {CONTRIBUTING.md (2)
43-43
: Minor grammar correction in documentation- - It is not currently possible to create a `Task`, `CheckedContinuation`, or `AsyncThrowingStream` with a specific error type. You will need to instead return a `Result` and then call its `.get()` method. + - It is not currently possible to create a `Task`, `CheckedContinuation`, or `AsyncThrowingStream` with a specific error type. You will need to instead return a `Result` and then call its `.get()` method.The possessive "its" is correct here, referring to the
Result
's.get()
method.🧰 Tools
🪛 LanguageTool
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return aResult
and then call its.get()
method. - `Dictionary.mapVal...(AI_HYDRA_LEO_CPT_ITS_IT)
45-46
: Consider improving punctuation in code examplesThere are loose punctuation marks before the code examples. Consider standardizing the approach to introducing code examples throughout the documentation.
- - There are times when the compiler struggles to infer the type of the error thrown within a `do` block. In these cases, you can disable type inference for a `do` block and explicitly specify the type of the thrown error, like: `do throws(InternalError) { … }`. - - The compiler will never infer the type of the error thrown by a closure; you will need to specify this yourself; e.g. `let items = try jsonValues.map { jsonValue throws(InternalError) in … }`. + - There are times when the compiler struggles to infer the type of the error thrown within a `do` block. In these cases, you can disable type inference for a `do` block and explicitly specify the type of the thrown error, like `do throws(InternalError) { … }`. + - The compiler will never infer the type of the error thrown by a closure; you will need to specify this yourself, e.g., `let items = try jsonValues.map { jsonValue throws(InternalError) in … }`.🧰 Tools
🪛 LanguageTool
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
Tests/AblyChatTests/Helpers/Helpers.swift (1)
43-66
: Evaluate naming clarity for the nestedCase
enum.
The nested enumCase
clarifies high-level error categories, which is good for typed error handling. However, consider giving it a more descriptive name (e.g.,InternalErrorType
) if you expect to expand it significantly.Sources/AblyChat/InternalError.swift (3)
3-31
: Ensure completeness ofInternalError
conversions.
The layout of.errorInfo(ARTErrorInfo)
and.other(Other)
is straightforward. If you plan on adding further specialized cases (beyondOther
), confirm you won’t lose vital metadata if you rely ontoARTErrorInfo()
for external usage.
45-49
: Minimal overhead withHeadersValue.JSONDecodingError
bridging.
This extension is straightforward. If you plan to add more detail (e.g., original JSON snippet) to the error, consider augmenting theother(...)
payload for richer debugging.
51-55
: Maintain consistent error detail.
JSONValueDecodingError
is mapped similarly toheadersValueJSONDecodingError
, which is good for uniformity. Consider standardizing error messages across these two for logs if they differ significantly in internal usage.Sources/AblyChat/Rooms.swift (4)
104-108
: Check concurrency race conditions inwaitForRoom()
.
AwaitingcreationTask.value
is done in an actor, which is good. Be mindful of external cancellations (e.g., a user callingrelease(roomID:)
), ensuring the canceled states are properly handled.
166-181
: Appropriate fallback towaitForRoom
.
Your fallback logic clarifies waiting on the existing map entry if the entry is already present. The debug logs are helpful. Consider adding a short doc snippet explaining the concurrency rationale for future maintainers.
263-283
: Effective approach to manage creation failure signals.
The usage ofAsyncStream<Result<Void, InternalError>>
is a neat pattern to unify a fail signal and normal release completion. Recommend adding stress tests to confirm that multiple signals or cancellations don’t cause unexpected behaviors.
335-336
: Use descriptive error on forced release.
Failing creation with a.roomReleasedBeforeOperationCompleted
message is helpful. If you see repeated confusion in user logs, consider adding more context about which room and operation triggered the release.Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift (1)
6-28
: Ensure consistency of error handling and consider avoidingfatalError
.
Although usingfatalError
(line 22) to handle programmer errors aligns with the current design comments, you could replace it with a custom assertion or descriptive error throw that gracefully fails without abruptly terminating. This helps maintain stability in environments where unexpected runtime termination is undesirable.- fatalError("ably-cocoa request threw an error - this indicates a programmer error") + assertionFailure("ably-cocoa request encountered an unexpected error; please check usage.") + throw InternalError(description: "Programmer error in AblyCocoa request")Sources/AblyChat/DefaultPresence.swift (1)
211-211
: Catch thrown errors during subscription callbacks.
In both subscription callbacks (lines 211 & 230), ifprocessPresenceSubscribe
throws, theTask
will silently fail. Consider wrapping withdo-catch
and logging or accommodating user-facing error handling for more graceful fallback.Task { - let presenceEvent = try processPresenceSubscribe(...) - subscription.emit(presenceEvent) + do { + let presenceEvent = try processPresenceSubscribe(...) + subscription.emit(presenceEvent) + } catch { + logger.log(message: "Error subscribing: \(error)", level: .error) + } }Also applies to: 230-230
Sources/AblyChat/RoomLifecycleManager.swift (1)
758-759
:_performAttachOperation
andperformAttachmentCycle
: watch concurrency states.
The code checks for in-progress operations and usesstatus.operationID
to coordinate concurrency. Ensure all transitions remain atomic if anything else tries to modifystatus
simultaneously (e.g., from other actor tasks).Also applies to: 764-785, 791-791
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
📒 Files selected for processing (42)
CONTRIBUTING.md
(1 hunks)Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift
(1 hunks)Sources/AblyChat/ChatAPI.swift
(7 hunks)Sources/AblyChat/DefaultMessages.swift
(7 hunks)Sources/AblyChat/DefaultOccupancy.swift
(1 hunks)Sources/AblyChat/DefaultPresence.swift
(5 hunks)Sources/AblyChat/DefaultRoomLifecycleContributor.swift
(1 hunks)Sources/AblyChat/DefaultRoomReactions.swift
(1 hunks)Sources/AblyChat/DefaultTyping.swift
(3 hunks)Sources/AblyChat/Errors.swift
(4 hunks)Sources/AblyChat/Extensions/Dictionary+Extensions.swift
(1 hunks)Sources/AblyChat/Headers.swift
(3 hunks)Sources/AblyChat/InternalError.swift
(1 hunks)Sources/AblyChat/JSONCodable.swift
(22 hunks)Sources/AblyChat/Message.swift
(2 hunks)Sources/AblyChat/Messages.swift
(6 hunks)Sources/AblyChat/Occupancy.swift
(2 hunks)Sources/AblyChat/PaginatedResult.swift
(3 hunks)Sources/AblyChat/Presence.swift
(7 hunks)Sources/AblyChat/PresenceDataDTO.swift
(1 hunks)Sources/AblyChat/Room.swift
(4 hunks)Sources/AblyChat/RoomFeature.swift
(2 hunks)Sources/AblyChat/RoomLifecycleManager.swift
(17 hunks)Sources/AblyChat/RoomReactionDTO.swift
(2 hunks)Sources/AblyChat/RoomReactions.swift
(1 hunks)Sources/AblyChat/Rooms.swift
(6 hunks)Sources/AblyChat/Typing.swift
(2 hunks)Tests/AblyChatTests/ChatAPITests.swift
(1 hunks)Tests/AblyChatTests/DefaultMessagesTests.swift
(3 hunks)Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
(1 hunks)Tests/AblyChatTests/DefaultRoomPresenceTests.swift
(6 hunks)Tests/AblyChatTests/DefaultRoomTypingTests.swift
(2 hunks)Tests/AblyChatTests/DefaultRoomsTests.swift
(2 hunks)Tests/AblyChatTests/Helpers/Helpers.swift
(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoom.swift
(2 hunks)Tests/AblyChatTests/Mocks/MockRoomFactory.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
(2 hunks)Tests/AblyChatTests/PresenceDataDTOTests.swift
(1 hunks)Tests/AblyChatTests/RoomReactionDTOTests.swift
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (31)
- Sources/AblyChat/DefaultRoomReactions.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Sources/AblyChat/PresenceDataDTO.swift
- Tests/AblyChatTests/PresenceDataDTOTests.swift
- Tests/AblyChatTests/DefaultRoomsTests.swift
- Tests/AblyChatTests/RoomReactionDTOTests.swift
- Sources/AblyChat/Errors.swift
- Tests/AblyChatTests/DefaultRoomPresenceTests.swift
- Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
- Sources/AblyChat/Headers.swift
- Sources/AblyChat/Extensions/Dictionary+Extensions.swift
- Sources/AblyChat/DefaultOccupancy.swift
- Sources/AblyChat/RoomReactions.swift
- Tests/AblyChatTests/DefaultMessagesTests.swift
- Tests/AblyChatTests/DefaultRoomTypingTests.swift
- Sources/AblyChat/RoomReactionDTO.swift
- Tests/AblyChatTests/Mocks/MockRoomFactory.swift
- Sources/AblyChat/DefaultRoomLifecycleContributor.swift
- Sources/AblyChat/RoomFeature.swift
- Sources/AblyChat/Message.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Sources/AblyChat/Typing.swift
- Tests/AblyChatTests/Mocks/MockRoom.swift
- Sources/AblyChat/Occupancy.swift
- Sources/AblyChat/DefaultMessages.swift
- Sources/AblyChat/Messages.swift
- Sources/AblyChat/Room.swift
- Sources/AblyChat/DefaultTyping.swift
- Sources/AblyChat/ChatAPI.swift
- Sources/AblyChat/Presence.swift
- Sources/AblyChat/PaginatedResult.swift
🧰 Additional context used
🪛 LanguageTool
CONTRIBUTING.md
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return a Result
and then call its .get()
method. - `Dictionary.mapVal...
(AI_HYDRA_LEO_CPT_ITS_IT)
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like: do throws(InternalError) { … }
. - The compiler will never infer the t...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g. let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: Example app, tvOS (Xcode 16)
- GitHub Check: Example app, iOS (Xcode 16)
- GitHub Check: Example app, macOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, tvOS (Xcode 16) - GitHub Check: Xcode, tvOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, iOS (Xcode 16) - GitHub Check: SPM (Xcode 16)
- GitHub Check: Xcode, iOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, macOS (Xcode 16) - GitHub Check: Xcode, macOS (Xcode 16)
🔇 Additional comments (34)
Tests/AblyChatTests/InternalErrorTests.swift (1)
1-24
: Good test coverage for InternalError to ARTErrorInfo conversionThe tests effectively verify both scenarios:
- When the underlying error is already ARTErrorInfo
- When the underlying error is a different error type
These tests align perfectly with the PR objective of ensuring the public API throws ARTErrorInfo consistently.
Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift (2)
67-80
: Appropriate error handling in attach methodThe method now throws
InternalError
instead ofARTErrorInfo
and properly converts any caught errors usingtoInternalError()
. This aligns with the PR objective of standardizing error types.
82-95
: Appropriate error handling in detach methodSimilar to the attach method, this implementation properly converts errors to
InternalError
and maintains consistency with the error handling approach.Tests/AblyChatTests/Mocks/MockFeatureChannel.swift (1)
25-35
: Appropriate error handling in waitToBeAbleToPerformPresenceOperations methodThe method has been updated to throw
InternalError
instead ofARTErrorInfo
and properly converts errors using thetoInternalError()
method. This implementation aligns with the PR objective of standardizing error types throughout the codebase.CONTRIBUTING.md (1)
35-47
: Excellent documentation on error handling approachThe new section provides comprehensive guidance on typed throws in the SDK, clearly explaining:
- The use of
ARTErrorInfo
in the public API- The use of
InternalError
internally- Swift's limitations with typed throws
- Workarounds for common scenarios
This documentation will be valuable for current and future contributors to understand the error handling approach.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~43-~43: “its” (belonging to it) seems less likely than “it”
Context: ...instead return aResult
and then call its.get()
method. - `Dictionary.mapVal...(AI_HYDRA_LEO_CPT_ITS_IT)
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
Tests/AblyChatTests/Helpers/Helpers.swift (3)
5-18
: Confirm consistency in error checks.
The updated logic conditionally castsmaybeError
to bothARTErrorInfo
andInternalError
to detect a chat error. Ensure that callsites handle both rawARTErrorInfo
and wrappedInternalError
consistently. Also, verifying test coverage forisChatError
with both error forms would be beneficial.Do you already have tests that pass in an
InternalError.errorInfo(...)
? If not, here's a recommended approach to add them for completeness.
35-41
: ValidateisInternalErrorWrappingErrorInfo
usage.
This helper is concise and appears correct. However, ensure all references where anInternalError
might embed non-matchingARTErrorInfo
(e.g., different code or domain) are tested. Otherwise, you risk false positives if anARTErrorInfo
is unexpectedly equal.
68-75
: Additional coverage forisInternalErrorWithCase
.
This function elegantly checks whether an error matches a specific internal error case. Consider augmenting test scenarios that pass in other error instances, verifying it returnsfalse
for non-matching types.Sources/AblyChat/InternalError.swift (3)
33-37
: Good extension for bridgingARTErrorInfo
toInternalError
.
This simple conversion accurately wraps externalARTErrorInfo
. Ensure that places converting fromARTErrorInfo
follow up with an appropriate typed error check if additional context is needed.
39-43
: Verify coverage forChatAPI.ChatError
conversion.
It’s beneficial thatChatAPI.ChatError
can now map toInternalError
. Confirm that each variant ofChatAPI.ChatError
has an appropriate integration test to ensure the bridging logic is exercised.
57-61
: ValidatePaginatedResultError
path.
MappingPaginatedResultError
to an.other(.paginatedResultError)
maintains type safety. If you discover any confusion around pagination or partial fetch errors, consider adding more explicit error codes or messages to facilitate debugging.Sources/AblyChat/Rooms.swift (6)
28-28
: Typed throws provides clarity.
Changingget(roomID:options:)
to throw(ARTErrorInfo)
explicitly conveys runtime error types, which is a good step toward safer Swift API design. Ensure your documentation highlights this typed-throw contract.
86-89
: Ensure creation task lifecycle is well-defined.
ThecreationTask
storingResult<RoomFactory.Room, InternalError>
is helpful for concurrency. Confirm that thefailCreation
closure is consistently invoked to avoid leaving any tasks hanging.
154-164
: Validate mismatch logic for room options.
The new check that throws aninconsistentRoomOptions
error is clear. Confirm all code paths that might override previously storedroomOptions
are tested to avoid legitimate overrides being flagged.
182-253
: Complex concurrency flow – proceed with caution.
The parallel tasks waiting for release completion or creation failure are well-coordinated withwithTaskGroup
. This is powerful but can be tricky. Carefully maintain logs and test coverage to prevent subtle concurrency bugs (e.g., attempting multiplefailCreation
calls from different threads).
256-260
: Re-check error translation toARTErrorInfo
.
Wrappingerror.toARTErrorInfo()
insidecatch
ensures external callers seeARTErrorInfo
. Keep an eye out for internal errors that might contain additional info lost in translation.
294-294
: Create room flow.
Creating the room and immediately storing it inroomStates
is correct. Just ensure a robust test that forcibly cancels the task before or duringcreateRoom
to confirm safety.Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift (4)
32-46
: Confirm async usage and concurrency correctness inattachAsync
.
ThewithCheckedContinuation
pattern to returnResult<Void, ARTErrorInfo>
looks proper, and the subsequent.get()
call ensures typed error propagation. Just ensure that any concurrent modifications to shared state are wrapped or guarded appropriately in the call sites.
48-62
: Confirm async usage and concurrency correctness indetachAsync
.
Same as withattachAsync
, the concurrency model appears correct, and typed errors are consistently converted intoInternalError
.
65-72
: ValidatePresenceMessage
structure compliance with concurrency.
While marking it as aSendable
version ofARTPresenceMessage
is correct, consider double-checking that all its fields conform to thread-safe usage, especially if they’re ever mutated after initialization.
88-171
: Overall consistency in typed async presence APIs.
All new presence methods (getAsync
,leaveAsync
,enterClientAsync
,updateAsync
) correctly usewithCheckedContinuation
and convertARTErrorInfo
intoInternalError
. The usage ofpreconditionFailure
for unexpected nil branches also appears purposeful. This uniform approach strengthens clarity.Sources/AblyChat/DefaultPresence.swift (4)
22-45
: Synchronized error conversion inget()
.
The logic properly waits for presence readiness and catches errors, translating them toARTErrorInfo
. The approach is consistent with the rest of the typed-throws design.
73-97
:isUserPresent
aligns well with typed-throws design.
The call togetAsync(ARTRealtimePresenceQuery(clientId: …))
logically complements the function’s purpose. No issues noted.
108-131
: Validate test coverage forenter(optionalData:)
.
Enforcing typed errors and logging are good. Confirm that your test suite includes coverage for success/failure paths, ensuring that presence calling code properly awaits and handles potential errors.
297-297
: Prompt consistency for error conversions.
processPresenceSubscribe
directly throwsARTErrorInfo
, while other methods rely on conversion toInternalError
. If uniform typed throws are desired throughout, consider converting or rethrowing asInternalError
here as well.Sources/AblyChat/RoomLifecycleManager.swift (4)
15-16
: Typing the attach/detach operations forInternalError
.
Switching fromARTErrorInfo
toInternalError
ensures consistent usage across the codebase. The asynchronous approach remains coherent with the concurrency patterns in the rest of the SDK.Also applies to: 44-45, 49-49
589-590
: Refined operation flow with typed continuation results.
EmbeddingInternalError
in the continuation and wait flow is sensible for orchestrating attach/detach across multiple contributors. This improves clarity on final error states.Also applies to: 653-655, 679-679, 700-703
817-830
: Graceful handling for suspended/failed states in attach/detach cycles.
This robust approach transitions the room status to suspended or failed while scheduling or executing follow-up operations. Its stepwise flow, with fallback cycles, seems thorough. A coverage check for partial failures (one channel fails while another succeeds) may help.Also applies to: 883-897, 905-905, 925-927, 940-940, 971-972
1220-1252
: Verifying presence operations readiness.
The logic aroundwaitToBeAbleToPerformPresenceOperations
checks for anattached
state or fails early if unreachable. This clarifies the state machine around presence operations.Sources/AblyChat/JSONCodable.swift (5)
8-10
: Protocol method signatures properly updated for typed throwsThe update to
init(jsonValue:)
to use typed throws withInternalError
is correctly implemented. This change aligns with the PR objective to standardize error handling in the public API.
25-27
: Protocol method signatures properly updated for typed throwsThe update to
init(jsonObject:)
to use typed throws withInternalError
is correctly implemented. This matches the approach used in theJSONDecodable
protocol.
38-44
: Error conversion implementation is consistentThe implementation correctly uses the
.toInternalError()
method to convertJSONValueDecodingError
toInternalError
. This ensures that all errors thrown from this protocol implementation are of the expected type.
58-68
: Error handling standardization in dictionary extensionThe method signature update and error conversion implementation for
objectValueForKey
is properly implemented. Both error cases now consistently throwInternalError
using the.toInternalError()
conversion method.
73-87
: Consistent error handling across all dictionary extension methodsAll dictionary extension methods have been consistently updated to use typed throws with
InternalError
and properly convert errors using.toInternalError()
. This approach is applied uniformly across all helper methods, ensuring a consistent error handling pattern.Also applies to: 94-104, 109-123, 130-140, 145-159, 166-176, 181-195, 202-212, 219-233, 244-248, 253-259, 275-279, 286-292, 294-300
Our current usage of `realtime.request` is quite messy at the call site, and will get more so when we need to use typed throws throughout the codebase for #43. So move it to a helper (I've changed it to use typed throws whilst I'm at it). This also opens the door to us using a RoomLifecycleContributorChannel-like protocol internally when making requests, which will simplify mocking.
Motivation as in 400617f. It's nice that the presence data and extras are now expressed in terms of JSONValue, which simplifies various call sites.
Motivation as in 4c7c5a8.
Motivation as in 4c7c5a8.
Motivation as in 4c7c5a8.
37d38ac
to
11d58da
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Sources/AblyChat/DefaultTyping.swift (1)
101-102
: 🛠️ Refactor suggestionEnsure
error.message
is valid for non-InternalError
values.If the thrown
error
isn’t anInternalError
orARTErrorInfo
, callingerror.message
may cause runtime issues. Consider fallback toerror.localizedDescription
to handle all error types safely.- logger.log(message: error.message, level: .error) + if let internalError = error as? InternalError { + logger.log(message: internalError.message, level: .error) + } else if let artError = error as? ARTErrorInfo { + logger.log(message: artError.message, level: .error) + } else { + logger.log(message: "\(error)", level: .error) + }
🧹 Nitpick comments (6)
CONTRIBUTING.md (1)
45-46
: Consider adjusting punctuation to avoid potential style issues.The dash (“-”) following the code snippet can cause punctuation style warnings. Replacing it with a comma, semicolon, or a full stop can improve clarity and eliminate the warning.
- like: `do throws(InternalError) { … }`. - The compiler will never infer the type... + like: `do throws(InternalError) { … }`. The compiler will never infer the type...🧰 Tools
🪛 LanguageTool
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
Example/AblyChatExample/Mocks/MockClients.swift (2)
73-75
: Unimplementeddetach
in mock
Currently callsfatalError("Not yet implemented")
. Consider returning a no-op or a made-up error for test stability rather than halting.-fatalError("Not yet implemented") +throw ARTErrorInfo.createMockError(description: "Mock detach not implemented.")
324-326
:isUserPresent
remains unimplemented
Marking this withfatalError
can disrupt test flows. Consider returning a predictable bool or throwing a mock error to facilitate testing edge cases.-fatalError("Not yet implemented") +return false // or +throw ARTErrorInfo.createMockError(description: "isUserPresent not implemented.")Sources/AblyChat/DefaultPresence.swift (2)
73-97
: Add unit tests forisUserPresent(clientID:)
.
While the logic is straightforward, including dedicated test coverage for both present and absent client scenarios would strengthen confidence.
210-210
: Possible unhandled errors within theTask
.
Because errors thrown inside the Task block can be lost without ado-catch
, consider wrappingtry processPresenceSubscribe(...)
to handle or log failures properly.Sources/AblyChat/RoomLifecycleManager.swift (1)
764-865
: Extend typed errors throughout attach cycle.
Converting attach failures intoInternalError
is coherent. Consider documenting partial attach scenarios if some contributors fail halfway through.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
📒 Files selected for processing (41)
CONTRIBUTING.md
(1 hunks)Example/AblyChatExample/Mocks/MockClients.swift
(13 hunks)Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift
(1 hunks)Sources/AblyChat/ChatAPI.swift
(7 hunks)Sources/AblyChat/DefaultMessages.swift
(6 hunks)Sources/AblyChat/DefaultOccupancy.swift
(1 hunks)Sources/AblyChat/DefaultPresence.swift
(5 hunks)Sources/AblyChat/DefaultRoomLifecycleContributor.swift
(1 hunks)Sources/AblyChat/DefaultRoomReactions.swift
(1 hunks)Sources/AblyChat/DefaultTyping.swift
(3 hunks)Sources/AblyChat/Errors.swift
(4 hunks)Sources/AblyChat/Extensions/Dictionary+Extensions.swift
(1 hunks)Sources/AblyChat/Headers.swift
(3 hunks)Sources/AblyChat/InternalError.swift
(1 hunks)Sources/AblyChat/JSONCodable.swift
(22 hunks)Sources/AblyChat/Message.swift
(2 hunks)Sources/AblyChat/Messages.swift
(6 hunks)Sources/AblyChat/Occupancy.swift
(2 hunks)Sources/AblyChat/PaginatedResult.swift
(3 hunks)Sources/AblyChat/Presence.swift
(7 hunks)Sources/AblyChat/PresenceDataDTO.swift
(1 hunks)Sources/AblyChat/Room.swift
(4 hunks)Sources/AblyChat/RoomFeature.swift
(2 hunks)Sources/AblyChat/RoomLifecycleManager.swift
(17 hunks)Sources/AblyChat/RoomReactionDTO.swift
(2 hunks)Sources/AblyChat/RoomReactions.swift
(1 hunks)Sources/AblyChat/Rooms.swift
(6 hunks)Sources/AblyChat/Typing.swift
(2 hunks)Tests/AblyChatTests/ChatAPITests.swift
(2 hunks)Tests/AblyChatTests/DefaultMessagesTests.swift
(2 hunks)Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
(1 hunks)Tests/AblyChatTests/DefaultRoomsTests.swift
(2 hunks)Tests/AblyChatTests/Helpers/Helpers.swift
(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoom.swift
(2 hunks)Tests/AblyChatTests/Mocks/MockRoomFactory.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
(2 hunks)Tests/AblyChatTests/PresenceDataDTOTests.swift
(1 hunks)Tests/AblyChatTests/RoomReactionDTOTests.swift
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (31)
- Tests/AblyChatTests/PresenceDataDTOTests.swift
- Sources/AblyChat/RoomReactions.swift
- Tests/AblyChatTests/DefaultRoomsTests.swift
- Tests/AblyChatTests/Mocks/MockRoom.swift
- Sources/AblyChat/Extensions/Dictionary+Extensions.swift
- Sources/AblyChat/Headers.swift
- Tests/AblyChatTests/DefaultMessagesTests.swift
- Tests/AblyChatTests/RoomReactionDTOTests.swift
- Tests/AblyChatTests/Mocks/MockRoomFactory.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Sources/AblyChat/DefaultRoomReactions.swift
- Sources/AblyChat/Typing.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
- Sources/AblyChat/Message.swift
- Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
- Sources/AblyChat/Occupancy.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Sources/AblyChat/Errors.swift
- Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
- Sources/AblyChat/RoomFeature.swift
- Sources/AblyChat/DefaultRoomLifecycleContributor.swift
- Sources/AblyChat/DefaultOccupancy.swift
- Sources/AblyChat/RoomReactionDTO.swift
- Sources/AblyChat/PresenceDataDTO.swift
- Tests/AblyChatTests/InternalErrorTests.swift
- Sources/AblyChat/PaginatedResult.swift
- Sources/AblyChat/Messages.swift
- Sources/AblyChat/Room.swift
- Sources/AblyChat/DefaultMessages.swift
- Sources/AblyChat/Presence.swift
- Sources/AblyChat/ChatAPI.swift
🧰 Additional context used
🪛 LanguageTool
CONTRIBUTING.md
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like: do throws(InternalError) { … }
. - The compiler will never infer the t...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g. let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: Example app, tvOS (Xcode 16)
- GitHub Check: Example app, iOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, tvOS (Xcode 16) - GitHub Check: Xcode, tvOS (Xcode 16)
- GitHub Check: SPM (Xcode 16)
- GitHub Check: Xcode, iOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, macOS (Xcode 16) - GitHub Check: Xcode, macOS (Xcode 16)
🔇 Additional comments (71)
Tests/AblyChatTests/Helpers/Helpers.swift (2)
8-33
: Looks good!The logic for detecting and matching
ARTErrorInfo
appears sound. The guard statement and subsequent checks effectively confirm appropriate error domain and codes.
35-38
:❓ Verification inconclusive
Verify that
ARTErrorInfo
supports equality checks.You're using
if case let .errorInfo(actualErrorInfo) = internalError, expectedErrorInfo == actualErrorInfo
. EnsureARTErrorInfo
implements equality or that you’ve added a custom extension to support this comparison.
🏁 Script executed:
#!/bin/bash # Description: Search for any extension or conformance of `ARTErrorInfo` to `Equatable`. fd -e swift --exec rg "struct|class|extension\s+ARTErrorInfo.*Equatable" {}Length of output: 12837
Action Required: Verify Equality Conformance for ARTErrorInfo
The code uses an equality check on ARTErrorInfo:
if let internalError = error as? InternalError, case let .errorInfo(actualErrorInfo) = internalError, expectedErrorInfo == actualErrorInfo { … }Our initial search (via the provided shell script) did not return clear evidence that ARTErrorInfo explicitly conforms to Equatable—or that there is a custom extension providing an equality implementation. Please manually verify (or update the implementation) to ensure that ARTErrorInfo supports equality comparisons as intended.
• Location: Tests/AblyChatTests/Helpers/Helpers.swift, Lines 35–38
Sources/AblyChat/InternalError.swift (1)
1-62
: Structured internal error handling looks great!Defining
InternalError
with.errorInfo
and.other
variants neatly unifies multiple error sources. Returning a well-definedARTErrorInfo
ensures consistent public exposure of error data.Sources/AblyChat/DefaultTyping.swift (2)
198-214
: Previous concurrency concern still applies.
106-107
:✅ Verification successful
Validate consistency of error conversion to
ARTErrorInfo
.The
catch
block rethrows usingerror.toARTErrorInfo()
. Confirm that all possible errors in the do-block are indeedInternalError
, to avoid runtime issues if another error type propagates.Also applies to: 148-149, 173-174
🏁 Script executed:
#!/bin/bash # Description: Search for async calls that might throw something other than `InternalError`. rg -A 4 "throws\(InternalError\)|throws\(ARTErrorInfo\)"Length of output: 75611
Ensure error conversion strictly handles InternalError.
In DefaultTyping.swift (lines 106–107, 148–149, and 173–174), the catch block rethrows errors viaerror.toARTErrorInfo()
. Our verification confirms that the surrounding do blocks are declared withthrows(InternalError)
, ensuring that only InternalError instances are expected. Please double-check that no other error types are inadvertently introduced into these blocks—for both current implementations and any future changes—so that the conversion remains safe and consistent.Sources/AblyChat/Rooms.swift (7)
28-28
: Use typed throw inget
for clearer error handling
Designatingthrows(ARTErrorInfo)
in the method signature provides explicit error semantics, making it clearer to callers what exceptions to expect.
86-89
: Refined error type usage inRoomMapEntry.requestAwaitingRelease
Switching from a generic error toInternalError
helps unify error handling and maintain consistency with the rest of the codebase.
105-112
: Consistent approach inwaitForRoom()
Usingasync throws(InternalError)
and retrieving theResult
viacreationTask.value.get()
cleanly bridges concurrency and typed error handling. No issues noted.
154-261
: Robust concurrency logic inget(roomID:options:)
The approach to synchronize room creation and release, handle in-progress states, and convertInternalError
toARTErrorInfo
ensures type-safe error propagation. ThewithTaskGroup
usage to wait for the first completed operation is a solid concurrency pattern.
264-283
: Neat creation-failure streaming mechanism
DefiningmakeCreationFailureFunctions()
to yield a failure viaAsyncStream
is an elegant way to coordinate inter-task failure signals. No concerns noted.
294-299
:createRoom
complementsget
perfectly
This helper adheres to the same error-handling policy, ensuring consistency across room creation code paths.
335-335
: Consistent bridging fromARTErrorInfo
toInternalError
Invoking.toInternalError()
on a newly createdARTErrorInfo
aligns with the rest of the file's typed-error strategy.Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift (9)
6-29
:requestAsync
leverages Swift concurrency effectively
UsingwithCheckedContinuation
for callback-based APIs is appropriate. The fallback tofatalError()
for programmer errors underscores the design intent that these exceptions should not typically occur in production.
32-46
:attachAsync
with typed error bridging
Attaching the channel with aResult<Void, ARTErrorInfo>
continuation and converting toInternalError
ensures consistency with higher-level error handling.
48-62
:detachAsync
follows the same pattern
Again, a clear use ofwithCheckedContinuation
, bridgingARTErrorInfo
→InternalError
. No issues noted.
65-87
: NewPresenceMessage
struct
Mapping fromARTPresenceMessage
into aSendable
struct is straightforward and safe. This avoids concurrency pitfalls when sharing presence data.
88-106
:getAsync()
presence retrieval
Capturing presence state with explicit typed errors fits well. The usage of.get()
on theResult
ensures typed error propagation.
107-123
:getAsync(_ query:)
variant
Same pattern as the no-parameter method, with typedInternalError
. Implementation is consistent and clear.
125-139
:leaveAsync(_:)
method
No concerns: checks for error, bridges toInternalError
, completes successfully otherwise.
141-155
:enterClientAsync
bridging
UsingwithCheckedContinuation
and bridgingARTErrorInfo
→InternalError
is consistent across these concurrency extensions.
157-171
:updateAsync
clarity
Retaining the callback-based logic but returningasync throws(InternalError)
improves code readability and error handling uniformity.Example/AblyChatExample/Mocks/MockClients.swift (20)
26-33
: Mockget
uses a typed throw
Switching tothrows(ARTErrorInfo)
for theget
method is consistent with the updated API expectations. Implementation is straightforward.
69-71
: Mockattach
partial implementation
Logging-only is fine for a mock. The typed throw aligns with the real method's signature.
125-127
: Mockget(options:)
Returning a basicMockMessagesPaginatedResult
is sufficient for testing scenarios.
129-145
: Mocksend
for messages
Emitting the newly createdMessage
to the subscription storage simulates sending behavior nicely.
147-163
: Mockupdate
for messages
This approach consistently updates the action to.update
and emits a fresh message object. Looks good.
165-181
: Mockdelete
for messages
Similarly sets.delete
action and emits the new state. No issues found.
214-224
: Mocksend
for reactions
Generating a newReaction
and emitting it is consistent with mock patterns.
261-263
: Mockget
in typing
Returning two random names fromMockStrings
is a valid approach for simulation.
265-267
: Mockstart
in typing
Emitting a typing event with the current client ID is sufficient for basic testing.
269-271
: Mockstop
in typing
Emitting an emptyTypingEvent
is similarly adequate for tests.
328-330
: Mock presenceenter()
Delegating toenter(dataForEvent: nil)
is consistent with typed-error refactoring.
332-334
: Mock presenceenter(data:)
Correctly funnels calls into the sharedenter(dataForEvent:)
helper. Looks good.
336-345
: Privateenter(dataForEvent:)
Emitting a.enter
event tomockSubscriptions
is a straightforward simulation of presence entering.
347-349
: Presenceupdate()
no-arg
Delegation toupdate(dataForEvent: nil)
preserves consistency. No concerns.
351-353
: Presenceupdate(data:)
Similarly delegates to the shared update function. Implementation is coherent.
355-364
: Privateupdate(dataForEvent:)
Emitting an.update
event ensures presence status changes can be captured.
366-368
: Presenceleave()
Again, nicely delegates toleave(dataForEvent: nil)
.
370-372
: Presenceleave(data:)
Consistent approach. No issues.
374-383
: Privateleave(dataForEvent:)
Properly emits a.leave
presence event. This addresses leaving logic well for mocking scenarios.
422-424
: Mockget
in occupancy
Provides a static occupancy event. Suitable for simple testing.Sources/AblyChat/DefaultPresence.swift (12)
22-44
: Consistent re-throwing withARTErrorInfo
.
The method correctly wrapsInternalError
errors and re-throws them asARTErrorInfo
. Logging is also well placed for troubleshooting.
47-69
: Parameterized presence retrieval is now fully utilized.
Passingparams.asARTRealtimePresenceQuery()
ensures the query parameters are respected, resolving previous concerns about unused parameters.
99-104
: Helper method usage is clear.
These lines simply delegate toenter(optionalData:)
, which improves maintainability by keeping logic centralized.
107-130
: Robust async presence enter flow.
This block cleanly waits for the room to be attachable before callingenterClientAsync
, then transforms and re-throws errors asARTErrorInfo
.
133-139
: Consistent delegation forupdate(data:)
.
Similar toenter()
, this pattern keeps exposed methods small and maintainable.
142-164
: Error conversion inupdate(optionalData:)
.
You are correctly logging the error and converting toARTErrorInfo
, maintaining consistent error-handling patterns.
167-173
: Delegation forleave(data:)
.
This matches the established style, increasing coherence across presence operations.
176-198
: Presence leave operation.
Re-throwingInternalError
asARTErrorInfo
maintains a uniform public interface. Logging statements are appropriate for debugging.
230-230
: Same potential unhandled exception in Task.
This invocation mirrors line 210’s pattern.
252-257
: Graceful handling of missing data indecodePresenceDataDTO()
.
Throwing an internal error whenpresenceData
isnil
makes the failure state explicit and ensures consistent error conversion.
267-295
: MappingPresenceMessage
to domain model.
Neatly handles missing client IDs or timestamps and logs errors with context. This is a good, defensive approach.
297-297
: Typed throws in subscription processing.
This move tothrows(InternalError)
ensures uniform error handling throughout.Sources/AblyChat/RoomLifecycleManager.swift (14)
14-16
: Transition tothrows(InternalError)
in channel protocol.
Using typed errors here unifies error handling at the protocol level and clarifies expected failure modes.
44-45
:RoomLifecycleManager
operations now use typed throws.
Shifting tothrows(InternalError)
for attach/detach/wait operations ensures internal errors are handled consistently.Also applies to: 49-49
589-590
: OperationResultContinuations now storingInternalError
.
Revising continuation results toResult<Void, InternalError>
centralizes error representation and prevents confusion.
653-655
: Typed error throw inwaitForCompletionOfOperationWithID
.
This clarifies that operation completions can propagateInternalError
to callers.
678-680
: Operation completion result withInternalError
.
Consistently capturing typed errors is helpful for reliability and debugging.
700-704
:performAnOperation
nowthrows(InternalError)
.
Adopting typed throws refines the contract for all room lifecycle operations.
746-759
: Typed attach operation.
Calls to_performAttachOperation
are now fully typed, aligning with the broader error-handling pattern.
865-871
: Refined detach error signature.
EnsuringperformDetachOperation
throwsInternalError
keeps operations symmetrical.
877-899
: Detachment cycle now typed.
Centralizing all detach failure paths aroundInternalError
clarifies how partial failures propagate.
924-989
: Partial contributor detachment handling.
Retry logic for non-failed contributors is robust. The approach to handling concurrency is sound here.
971-972
: Detachment errors rethrown asInternalError
.
This final ensures consistent error form across the entire cycle.
1075-1075
:performRetryOperation
typed throw.
Handling suspended channels under a uniform error type simplifies retry logic.
1178-1187
:performRundownOperation
error alignment.
Equally adoptingInternalError
for the RUNDOWN flow fosters consistency with attach/detach.
1220-1251
: Presence operation readiness checks.
ThrowingpresenceOperationRequiresRoomAttach
clarifies the precondition needed (attached room) and improves developer feedback.Sources/AblyChat/JSONCodable.swift (4)
9-9
: Protocols updated to throwInternalError
.
Applying typed throws inJSONDecodable
andJSONObjectDecodable
clarifies decoding failure modes.Also applies to: 26-26, 38-38
58-220
: Dictionary extensions now throwInternalError
.
This uniform approach ensures decoding mistakes produce consistent internal exceptions instead of generic errors.
245-253
: Date extraction with typed errors.
Interpreting Ably’s epoch-based timestamps now explicitly returns anInternalError
on invalid input.
275-286
: Safe raw-value decoding with typed errors.
Providing a clear error ifinit(rawValue:)
fails improves debugging and user feedback.Also applies to: 294-294
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some questions.
This updates our public API to use typed throws, and specifically to always throw ARTErrorInfo. This makes us consistent with ably-cocoa, and allows users to access the error's `statusCode` to make decisions about whether they can retry the action that caused the error. I've gone for a light-touch approach of not changing any of the various error types that exist inside the SDK; inside I've hidden them all behind a common InternalError type, which can be converted to an ARTErrorInfo. We can revisit this in #32 (i.e. should we keep this rich internal Swift error types or just throw ARTErrorInfo internally?) Resolves #43.
11d58da
to
c09ac03
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
📒 Files selected for processing (41)
CONTRIBUTING.md
(1 hunks)Example/AblyChatExample/Mocks/MockClients.swift
(13 hunks)Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift
(1 hunks)Sources/AblyChat/ChatAPI.swift
(7 hunks)Sources/AblyChat/DefaultMessages.swift
(6 hunks)Sources/AblyChat/DefaultOccupancy.swift
(1 hunks)Sources/AblyChat/DefaultPresence.swift
(5 hunks)Sources/AblyChat/DefaultRoomLifecycleContributor.swift
(1 hunks)Sources/AblyChat/DefaultRoomReactions.swift
(1 hunks)Sources/AblyChat/DefaultTyping.swift
(3 hunks)Sources/AblyChat/Errors.swift
(4 hunks)Sources/AblyChat/Extensions/Dictionary+Extensions.swift
(1 hunks)Sources/AblyChat/Headers.swift
(3 hunks)Sources/AblyChat/InternalError.swift
(1 hunks)Sources/AblyChat/JSONCodable.swift
(22 hunks)Sources/AblyChat/Message.swift
(2 hunks)Sources/AblyChat/Messages.swift
(6 hunks)Sources/AblyChat/Occupancy.swift
(2 hunks)Sources/AblyChat/PaginatedResult.swift
(3 hunks)Sources/AblyChat/Presence.swift
(7 hunks)Sources/AblyChat/PresenceDataDTO.swift
(1 hunks)Sources/AblyChat/Room.swift
(4 hunks)Sources/AblyChat/RoomFeature.swift
(2 hunks)Sources/AblyChat/RoomLifecycleManager.swift
(17 hunks)Sources/AblyChat/RoomReactionDTO.swift
(2 hunks)Sources/AblyChat/RoomReactions.swift
(1 hunks)Sources/AblyChat/Rooms.swift
(6 hunks)Sources/AblyChat/Typing.swift
(2 hunks)Tests/AblyChatTests/ChatAPITests.swift
(2 hunks)Tests/AblyChatTests/DefaultMessagesTests.swift
(2 hunks)Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
(1 hunks)Tests/AblyChatTests/DefaultRoomsTests.swift
(2 hunks)Tests/AblyChatTests/Helpers/Helpers.swift
(2 hunks)Tests/AblyChatTests/InternalErrorTests.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoom.swift
(2 hunks)Tests/AblyChatTests/Mocks/MockRoomFactory.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
(1 hunks)Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
(2 hunks)Tests/AblyChatTests/PresenceDataDTOTests.swift
(1 hunks)Tests/AblyChatTests/RoomReactionDTOTests.swift
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (33)
- Sources/AblyChat/Headers.swift
- Sources/AblyChat/PresenceDataDTO.swift
- Sources/AblyChat/Extensions/Dictionary+Extensions.swift
- Sources/AblyChat/Errors.swift
- Tests/AblyChatTests/PresenceDataDTOTests.swift
- Sources/AblyChat/RoomReactions.swift
- Tests/AblyChatTests/RoomReactionDTOTests.swift
- Sources/AblyChat/DefaultRoomReactions.swift
- Tests/AblyChatTests/ChatAPITests.swift
- Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
- Tests/AblyChatTests/DefaultRoomsTests.swift
- Sources/AblyChat/Occupancy.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift
- Sources/AblyChat/RoomReactionDTO.swift
- Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift
- Tests/AblyChatTests/DefaultMessagesTests.swift
- Tests/AblyChatTests/Mocks/MockRoom.swift
- Sources/AblyChat/Typing.swift
- Sources/AblyChat/DefaultRoomLifecycleContributor.swift
- Tests/AblyChatTests/InternalErrorTests.swift
- Tests/AblyChatTests/Mocks/MockRoomLifecycleManager.swift
- Tests/AblyChatTests/Mocks/MockRoomFactory.swift
- Sources/AblyChat/Message.swift
- Sources/AblyChat/DefaultOccupancy.swift
- Sources/AblyChat/Room.swift
- Sources/AblyChat/RoomFeature.swift
- Sources/AblyChat/DefaultMessages.swift
- Sources/AblyChat/Presence.swift
- Sources/AblyChat/RoomLifecycleManager.swift
- Example/AblyChatExample/Mocks/MockClients.swift
- Sources/AblyChat/Messages.swift
- Sources/AblyChat/PaginatedResult.swift
- Sources/AblyChat/JSONCodable.swift
🧰 Additional context used
🪛 LanguageTool
CONTRIBUTING.md
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like: do throws(InternalError) { … }
. - The compiler will never infer the t...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g. let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms (11)
- GitHub Check: Example app, tvOS (Xcode 16)
- GitHub Check: Example app, iOS (Xcode 16)
- GitHub Check: Example app, macOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, tvOS (Xcode 16) - GitHub Check: Xcode, tvOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, iOS (Xcode 16) - GitHub Check: Xcode, iOS (Xcode 16)
- GitHub Check: Xcode,
release
configuration, macOS (Xcode 16) - GitHub Check: Xcode, macOS (Xcode 16)
- GitHub Check: SPM (Xcode 16)
- GitHub Check: SPM,
release
configuration (Xcode 16)
🔇 Additional comments (30)
CONTRIBUTING.md (1)
35-47
: Great addition of error handling documentation!This new "Throwing errors" section provides clear guidance on the error handling strategy - using
ARTErrorInfo
for public API andInternalError
internally. The section about Swift's typed throws limitations and workarounds is particularly helpful for contributors.🧰 Tools
🪛 LanguageTool
[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...ify the type of the thrown error, like:do throws(InternalError) { … }
. - The compiler will never infer the t...(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ill need to specify this yourself; e.g.let items = try jsonValues.map { jsonValue throws(InternalError) in … }
. ### Testing guidelines #### Exposin...(UNLIKELY_OPENING_PUNCTUATION)
Tests/AblyChatTests/Helpers/Helpers.swift (4)
5-22
: Well-implemented error checking enhancementsThe improved
isChatError
function now properly handles both directARTErrorInfo
errors andInternalError
instances that wrap anARTErrorInfo
. This makes the test helpers compatible with the new error handling approach.
35-41
: Good addition of specialized error checkingThe new
isInternalErrorWrappingErrorInfo
function provides a clean way to verify if an error is anInternalError
wrapping a specificARTErrorInfo
. This will make tests more specific and readable.
43-66
: Well-designed error categorizationThe
InternalError.Case
enum provides a clean way to categorize different types of internal errors, enhancing testing capabilities. TheenumCase
property enables convenient access to the specific case of an error without complex pattern matching in tests.
68-74
: Useful helper for error case checkingThe
isInternalErrorWithCase
function complements the error case system, allowing for concise checks of error types in tests without boilerplate casting and pattern matching.Sources/AblyChat/InternalError.swift (2)
3-31
: Well-structured error handling implementationThe
InternalError
enum provides a clean, unified approach to error handling. The documentation explains the current design and future plans well. ThetoARTErrorInfo()
method provides a clear conversion path from internal errors to public API errors.
33-61
: Good extensions for error conversionThese extensions provide a consistent way to convert various error types to
InternalError
. This approach reduces boilerplate code throughout the codebase and ensures uniform error handling.Sources/AblyChat/DefaultTyping.swift (5)
85-107
: Improved error typing inget()
methodThe method now correctly uses typed throws with
ARTErrorInfo
for the public API and handlesInternalError
internally. The implementation has been simplified by usinggetAsync()
directly.
111-149
: Well-structured error handling instart()
methodThe method now uses a continuation pattern that returns a
Result<Void, InternalError>
and properly converts internal errors toARTErrorInfo
at the public API boundary. The implementation is clean and consistent with the new error handling approach.
153-174
: Improved error handling instop()
methodThe method now properly throws typed errors (
ARTErrorInfo
) and handlesInternalError
internally, maintaining consistency with the overall error handling approach in the SDK.
182-194
: Good error handling inprocessPresenceGet
The method now properly throws
InternalError
and convertsARTErrorInfo
toInternalError
using the new extension method, maintaining consistency with the overall error handling approach.
198-204
: Note about error propagation in TaskThis code throws an error inside a Task, but since the function isn't async, the error won't propagate to the caller. However, this is existing behavior from before this PR and outside the current scope of changes.
Is this error handling pattern intended? Since the function isn't
async
, errors thrown within the Task won't propagate to the original caller. Consider adding a comment explaining this design decision or creating a separate issue to address this in the future.Sources/AblyChat/Rooms.swift (5)
28-28
: The protocol method signature is correctly updated to throwARTErrorInfo
.The
get
method now explicitly throwsARTErrorInfo
instead of a genericError
, which aligns with the PR objective to use typed throws in the public API.
86-88
: Good update of theRoomMapEntry
enum to useInternalError
.The change from generic
Error
to the more specificInternalError
in both thecreationTask
result type and thefailCreation
closure parameter improves type safety and error handling clarity.
105-105
:waitForRoom()
method now throwsInternalError
as expected.This change correctly aligns with the internal error handling strategy where internal methods throw
InternalError
.
154-260
: Good implementation of the error handling pattern in theget
method.The implementation correctly uses
do throws(InternalError)
for internal error handling and converts toARTErrorInfo
at the public API boundary. The code correctly maintains the error conversion flow and preserves the detailed errors.
294-294
:createRoom
method now throwsInternalError
correctly.This internal method has been updated to specifically throw
InternalError
, which aligns with the internal error handling strategy.Sources/AblyChat/AblyCocoaExtensions/Ably+Concurrency.swift (4)
6-29
: Well-implementedrequestAsync
method with proper error handling.This new method extends
ARTRealtimeInstanceMethodsProtocol
with an asynchronous HTTP request capability that properly handles and converts errors. The implementation useswithCheckedContinuation
correctly to bridge the callback-based API to Swift's async/await pattern.
32-63
: Good update ofattachAsync
anddetachAsync
methods to throwInternalError
.Both methods now correctly use
InternalError
for error handling, which aligns with the internal error strategy in the codebase.
65-86
: Well-designedPresenceMessage
struct to ensureSendable
compliance.The new
PresenceMessage
struct provides aSendable
version ofARTPresenceMessage
, which is important for concurrency safety. The implementation correctly includes only the properties needed for the Chat SDK and provides a clean conversion method from the Ably Cocoa type.
88-171
: Comprehensive set of async presence methods with proper error handling.The extension adds several asynchronous methods to
ARTRealtimePresenceProtocol
that correctly handle errors and provide a modern Swift API surface. The implementation ofupdateAsync
correctly calls theupdate
method as expected.Sources/AblyChat/ChatAPI.swift (4)
13-13
: Public method signatures correctly updated to throwInternalError
.The method signatures for
getMessages
,sendMessage
,updateMessage
,deleteMessage
, andgetOccupancy
have been properly updated to specify that they throwInternalError
, which aligns with the internal error handling strategy.Also applies to: 43-43, 82-82, 134-134, 172-172
45-46
: Proper error conversion toInternalError
in client ID validation.The error handling for missing client ID now correctly converts
ARTErrorInfo
toInternalError
using thetoInternalError()
method, maintaining the error type consistency.Also applies to: 84-85
177-193
: Good refactoring ofmakeRequest
to userequestAsync
and proper error handling.The implementation now uses the new
requestAsync
method and correctly handles errors by throwingInternalError
when needed. This simplifies the error handling path and maintains type consistency.
198-205
: UpdatedmakePaginatedRequest
with proper error handling.The method now correctly specifies that it throws
InternalError
and uses typed throw annotations in the mapping closure, ensuring error type consistency throughout the API.Sources/AblyChat/DefaultPresence.swift (5)
22-22
: Public method signatures correctly updated to throwARTErrorInfo
.All public methods in the
DefaultPresence
class have been updated to specify that they throwARTErrorInfo
, which aligns with the PR objective to use typed throws in the public API.Also applies to: 47-47, 73-73, 99-99, 103-103, 133-133, 137-137, 167-167, 171-171
23-44
: Good implementation of the error handling pattern in all presence methods.Each method correctly uses
do throws(InternalError)
blocks for internal error handling and converts toARTErrorInfo
at the public API boundary. The implementation also properly uses the new async methods fromARTRealtimePresenceProtocol
.Also applies to: 48-69, 74-96, 109-130, 143-164, 177-198
252-265
: Proper error handling indecodePresenceDataDTO
.The method now correctly throws
InternalError
and converts errors appropriately, maintaining consistency with the rest of the error handling strategy.
267-295
: UpdatedprocessPresenceGet
to work withPresenceMessage
and proper error handling.The method now accepts
PresenceMessage
instead ofARTPresenceMessage
and properly throwsInternalError
, aligning with the changes in the rest of the codebase.
297-297
: UpdatedprocessPresenceSubscribe
to work withPresenceMessage
.The method signature has been updated to accept
PresenceMessage
directly, which works well with the newPresenceMessage
struct and improves the code's clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This updates our public API to use typed throws, and specifically to always throw
ARTErrorInfo
. This makes us consistent with ably-cocoa, and allows users to access the error'sstatusCode
to make decisions about whether they can retry the action that caused the error.See commit messages for more details.
Resolves #43.
Summary by CodeRabbit
Documentation
Refactor
Tests