Skip to content

Conversation

@ChiragAgg5k
Copy link
Member

@ChiragAgg5k ChiragAgg5k commented Oct 21, 2025

Summary

  • Add onError and onClose callback handlers to the Swift Realtime service
  • Allow developers to register custom callbacks for error and close events
  • Invoke callbacks when connection errors occur or connection closes

Changes

  • Added onErrorCallback and onCloseCallback private properties
  • Added public onError(_:) and onClose(_:) methods to register callbacks
  • Integrated callback invocations in WebSocketClientDelegate methods

Benefits

This enhancement enables better error handling and connection state management for Swift applications using the Realtime service.

Summary by CodeRabbit

  • New Features
    • Added support for registering a custom error handler for real-time connections, allowing apps to capture and react to errors with additional context.
    • Added support for registering a custom connection-close handler, enabling apps to detect and respond when a real-time connection closes (including before reconnection attempts).

This change adds support for custom error and close event handlers in the Swift Realtime implementation. Developers can now register callbacks to be notified when connection errors occur or when the connection closes, enabling better error handling and connection state management in applications.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds two public callback registration methods to Realtime: onError(_:) accepting (Swift.Error?, HTTPResponseStatus?) -> Void and onClose(_:) accepting () -> Void. Introduces private storage for these callbacks and invokes them from the existing onError(error:status:) and onClose(channel:data:) methods. No other behavioral changes to event handling are introduced.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Changes are limited to one file and are repetitive additions following existing callback patterns. Review should confirm API signatures, callback storage/invocation, and any thread-safety or consistency concerns with existing callbacks.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add error and close callbacks to Swift Realtime service" directly and accurately reflects the main changes in the changeset. The raw summary confirms that two new public callback registration methods (onError(_:) and onClose(_:)) are being added to the Realtime service, which aligns perfectly with the title. The title is clear, specific, and concise—it communicates the primary change without vague terminology, noise, or unnecessary details. A developer scanning the repository history would immediately understand that this PR introduces callback functionality for error and close events in the Swift Realtime service.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-swift-realtime-callbacks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ChiragAgg5k ChiragAgg5k requested a review from Copilot October 21, 2025 06:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds error and close callback handlers to the Swift Realtime service, allowing developers to register custom callbacks for monitoring connection state changes and handling errors. This enables better error handling and connection lifecycle management in Swift applications.

  • Adds onError(_:) and onClose(_:) public methods for registering custom callbacks
  • Integrates callback invocations into WebSocketClientDelegate methods when connection events occur

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
templates/swift/Sources/Services/Realtime.swift.twig (1)

29-35: API design is clean and follows Swift conventions.

The callback registration methods are well-designed with appropriate signatures. The single-callback design (where each call replaces the previous callback) is reasonable for this use case.

Consider adding documentation to clarify:

  • When these callbacks are invoked (connection errors, connection closure)
  • That registering a new callback replaces the previous one
  • Thread safety considerations (especially once synchronization is added per the previous comment)

Example:

+    /// Registers a callback to be invoked when a WebSocket error occurs.
+    /// Calling this method replaces any previously registered error callback.
+    /// - Parameter callback: A closure called with the error and HTTP status (if available).
     public func onError(_ callback: @escaping (Swift.Error?, HTTPResponseStatus?) -> Void) {
         self.onErrorCallback = callback
     }
     
+    /// Registers a callback to be invoked when the WebSocket connection closes.
+    /// Calling this method replaces any previously registered close callback.
+    /// - Parameter callback: A closure called when the connection closes.
     public func onClose(_ callback: @escaping () -> Void) {
         self.onCloseCallback = callback
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4f514 and ac43a8b.

📒 Files selected for processing (1)
  • templates/swift/Sources/Services/Realtime.swift.twig (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: build (8.3, PHP80)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, CLINode16)
  • GitHub Check: build (8.3, DotNet60)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: swift (server)
  • GitHub Check: android (client)
  • GitHub Check: apple (client)
🔇 Additional comments (2)
templates/swift/Sources/Services/Realtime.swift.twig (2)

225-225: Callback invocation placement is appropriate.

Invoking the close callback early in the method ensures users are notified immediately when the connection closes, before any reconnection logic executes.

Consider wrapping the callback invocation in error handling to prevent unexpected crashes:

-        onCloseCallback?()
+        do {
+            onCloseCallback?()
+        } catch {
+            print("Close callback failed: \(error.localizedDescription)")
+        }

While the callback signature is non-throwing, defensive error handling can prevent crashes if the callback performs unexpected operations or force-throws.


247-247: Callback invocation correctly passes error parameters.

The error callback is invoked with both the error and HTTP status, providing users with complete error information.

Consider the same defensive error handling as suggested for the close callback:

-        onErrorCallback?(error, status)
+        do {
+            onErrorCallback?(error, status)
+        } catch {
+            print("Error callback failed: \(error.localizedDescription)")
+        }

This prevents cascading failures if the user's error handling callback itself encounters issues.

ChiragAgg5k and others added 2 commits October 21, 2025 12:08
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@ChiragAgg5k ChiragAgg5k requested a review from Copilot October 21, 2025 06:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
templates/swift/Sources/Services/Realtime.swift.twig (1)

26-27: Add synchronization for thread-safe callback access.

These callback arrays are accessed from multiple threads without synchronization:

  • Written by user code when calling onError(_:) or onClose(_:) (potentially any thread) at lines 30 and 34
  • Read by WebSocket delegate methods (background thread) at lines 226 and 246

This creates a race condition that can lead to crashes or undefined behavior.

Apply this diff to protect the arrays using the existing connectSync queue:

-    private var onErrorCallbacks: [((Swift.Error?, HTTPResponseStatus?) -> Void)] = []
-    private var onCloseCallbacks: [(() -> Void)] = []
+    private var _onErrorCallbacks: [((Swift.Error?, HTTPResponseStatus?) -> Void)] = []
+    private var _onCloseCallbacks: [(() -> Void)] = []

Then update the public methods to use synchronized access:

 public func onError(_ callback: @escaping (Swift.Error?, HTTPResponseStatus?) -> Void) {
-    self.onErrorCallbacks.append(callback)
+    connectSync.sync {
+        self._onErrorCallbacks.append(callback)
+    }
 }
 
 public func onClose(_ callback: @escaping () -> Void) {
-    self.onCloseCallbacks.append(callback)
+    connectSync.sync {
+        self._onCloseCallbacks.append(callback)
+    }
 }

And at invocation sites (lines 226, 246):

-            onCloseCallbacks.forEach { $0() }
+            let callbacks = connectSync.sync { self._onCloseCallbacks }
+            callbacks.forEach { $0() }
-        onErrorCallbacks.forEach { $0(error, status) }
+        let callbacks = connectSync.sync { self._onErrorCallbacks }
+        callbacks.forEach { $0(error, status) }
🧹 Nitpick comments (1)
templates/swift/Sources/Services/Realtime.swift.twig (1)

29-35: Consider adding callback unregistration mechanism.

The current API allows registering callbacks but provides no way to unregister them. Callbacks will accumulate for the lifetime of the Realtime instance, which could lead to memory issues if callbacks capture strong references or if many callbacks are registered and unregistered frequently.

Consider returning an opaque token or subscription object that allows unregistering:

public struct CallbackToken {
    fileprivate let id: UUID
}

public func onError(_ callback: @escaping (Swift.Error?, HTTPResponseStatus?) -> Void) -> CallbackToken {
    let token = CallbackToken(id: UUID())
    connectSync.sync {
        self._onErrorCallbacks[token.id] = callback
    }
    return token
}

public func removeErrorCallback(_ token: CallbackToken) {
    connectSync.sync {
        self._onErrorCallbacks.removeValue(forKey: token.id)
    }
}

Note: This would require changing the storage from arrays to dictionaries.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c88c9a9 and e278eb2.

📒 Files selected for processing (1)
  • templates/swift/Sources/Services/Realtime.swift.twig (3 hunks)
🔇 Additional comments (2)
templates/swift/Sources/Services/Realtime.swift.twig (2)

225-229: Close callback timing is correct.

The callbacks are invoked only when reconnect is false, which occurs during intentional disconnections (e.g., when activeChannels is empty or manual disconnect via closeSocket). During automatic reconnection attempts, reconnect remains true and the callbacks are not invoked. This design appears correct as it prevents spurious close notifications during transient network issues.


242-247: Error callback invocation looks correct.

The error callbacks are invoked after logging the error, which is appropriate. Each registered callback receives both the error and HTTP status for comprehensive error handling.

Copy link
Member

Choose a reason for hiding this comment

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

Should we add a e2e test for it? Is it easy?

@ChiragAgg5k ChiragAgg5k merged commit 997e27a into master Oct 21, 2025
52 checks passed
@ChiragAgg5k ChiragAgg5k deleted the add-swift-realtime-callbacks branch October 21, 2025 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants