Skip to content
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-4948] Spec complete for Presence + Occupancy (excluding room status behaviour) #113

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

umair-ably
Copy link
Collaborator

@umair-ably umair-ably commented Nov 15, 2024

  • There seemed to be redundant duplication in the Presence protocol... I've just streamlined this by having an optional Data param where possible
  • Excluding spec points relating to room status behaviour, this is spec complete for Presence and Occupancy. I'll add the room status behaviour in a follow up PR once that's ready
  • Added integration tests

Will add unit tests as part of a separate PR/ticket

Summary by CodeRabbit

  • New Features

    • Enhanced presence and occupancy management in the chat application.
    • Introduced DefaultOccupancy and DefaultPresence classes for improved event handling.
    • Added support for real-time presence updates and occupancy checks.
  • Documentation

    • Improved comments in RoomOptions and PresenceOptions to clarify configuration details.
  • Tests

    • Expanded integration tests to cover occupancy and presence functionalities, ensuring expected behavior during user interactions.

Copy link

coderabbitai bot commented Nov 15, 2024

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

The pull request includes updates to the AblyChat project, focusing on enhancing presence and occupancy management. Key changes involve modifications to several files, including the introduction of new classes for handling occupancy and presence, updates to existing methods for improved functionality, and adjustments to the Package.resolved file to reflect new dependency revisions. The changes aim to provide a more comprehensive management of user presence and occupancy within the chat application.

Changes

File Path Change Summary
AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved Updated originHash and revision for ably-cocoa dependency.
Example/AblyChatExample/ContentView.swift Modified room() and showPresence() functions to enhance presence and occupancy management.
Example/AblyChatExample/Mocks/MockClients.swift Updated MockPresence methods for structured handling of presence data.
Example/AblyChatExample/Mocks/MockRealtime.swift Added presence property to Channel class, currently unimplemented.
Sources/AblyChat/DefaultOccupancy.swift Introduced DefaultOccupancy class for managing occupancy events.
Sources/AblyChat/DefaultPresence.swift Added DefaultPresence class for managing user presence in chat rooms.
Sources/AblyChat/Dependencies.swift Modified getChannel function for improved channel options resolution.
Sources/AblyChat/Occupancy.swift Introduced OccupancyEvent struct for occupancy data representation.
Sources/AblyChat/Presence.swift Redefined presence data structure and updated related protocols.
Sources/AblyChat/Room.swift Enhanced Room protocol and DefaultRoom class for managing presence and occupancy features.
Sources/AblyChat/RoomFeature.swift Updated RoomFeature enum for streamlined channel name logic.
Sources/AblyChat/RoomOptions.swift Added comments for clarity on presence options configuration.
Tests/AblyChatTests/IntegrationTests.swift Expanded integration tests to cover occupancy and presence functionalities.
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift Added presence property to MockRealtimeChannel class.

Assessment against linked issues

Objective Addressed Explanation
Implement online status (occupancy and presence) (ECO-4948)

Possibly related issues

  • ECO-4948: The changes directly implement functionalities for online status, occupancy, and presence management as outlined in the issue.

Possibly related PRs

Suggested reviewers

  • maratal

🐰 In the chat rooms, we hop and play,
With presence and occupancy, we brighten the day!
New classes and methods, oh what a delight,
Our chat app's now ready, to take flight!
Let's share our status, "📱 Online" we say,
With friends all around, come join the fray! 🐇


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@umair-ably umair-ably changed the title [ECO-4948] [DRAFT] Spec complete for Presence [ECO-4948] [DRAFT] Spec complete for Presence + Occupancy Nov 18, 2024
@umair-ably umair-ably force-pushed the ECO-4948 branch 5 times, most recently from 86de60e to e557487 Compare November 18, 2024 14:01
@umair-ably umair-ably changed the title [ECO-4948] [DRAFT] Spec complete for Presence + Occupancy [ECO-4948] Spec complete for Presence + Occupancy (excluding room status behaviour) Nov 18, 2024
@umair-ably umair-ably marked this pull request as ready for review November 18, 2024 14:19
@umair-ably
Copy link
Collaborator Author

@coderabbitai pause

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Outside diff range and nitpick comments (15)
Sources/AblyChat/Dependencies.swift (1)

35-40: Consider using property copying loop for maintainability

The current implementation manually copies specific properties from opts. This approach might miss properties if new ones are added to ARTRealtimeChannelOptions in the future.

Consider using Swift's Mirror API for a more maintainable approach:

-        if let customOpts = opts {
-            resolvedOptions.modes = customOpts.modes
-            resolvedOptions.cipher = customOpts.cipher
-            resolvedOptions.attachOnSubscribe = customOpts.attachOnSubscribe
-        }
+        if let customOpts = opts {
+            // Use Mirror to copy all properties except 'params' which was handled separately
+            let mirror = Mirror(reflecting: customOpts)
+            for child in mirror.children {
+                if child.label != "params",
+                   let value = child.value as? Any {
+                    resolvedOptions.setValue(value, forKey: child.label!)
+                }
+            }
+        }
Sources/AblyChat/RoomFeature.swift (1)

25-28: Consider documenting the planned typing channel format.

While the .typing case is not yet implemented, it would be helpful to document the planned channel format in the comment, similar to how other features are documented. This would maintain consistency in documentation and help future implementers.

Example/AblyChatExample/Mocks/MockRealtime.swift (1)

Line range hint 1-3: Track TODO for mock removal.

The file contains a TODO comment indicating this mock should be removed once API key integration is implemented in the example app.

Would you like me to create a GitHub issue to track the removal of this mock implementation once API key integration is complete?

Tests/AblyChatTests/IntegrationTests.swift (2)

132-172: LGTM: Comprehensive presence testing with thorough validation

The presence tests cover all essential scenarios (enter, update, leave) with proper custom data validation for both tx and rx clients.

Consider reducing code duplication by extracting the common test logic:

func verifyPresenceEvent(
    room: Room, 
    action: PresenceAction,
    subscription: AsyncStream<PresenceEvent>,
    customData: [String: Any]
) async throws {
    try await room.presence.performAction(action, data: .init(userCustomData: customData))
    let event = try await subscription.first { _ in true }
    #expect(event.action == action)
    #expect(event.data?.userCustomData?["randomData"]?.value as? String == customData["randomData"])
}

Line range hint 1-191: Consider adding error scenarios and edge cases

While the happy path testing is thorough, consider adding these scenarios:

  1. Presence conflicts (same client entering multiple times)
  2. Network disconnection handling during presence updates
  3. Race conditions between presence and occupancy updates
  4. Invalid custom data formats
  5. Maximum presence member limits

Would you like me to provide example test cases for these scenarios?

Example/AblyChatExample/Mocks/MockClients.swift (1)

301-302: Consider implementing missing methods for complete test coverage

Several methods in the mock implementation are still unimplemented (e.g., isUserPresent). Consider implementing these methods to ensure comprehensive integration testing.

Would you like help implementing the missing methods to improve test coverage?

Example/AblyChatExample/ContentView.swift (1)

197-203: Refactor presence message handling for better maintainability

The presence message construction logic could be extracted to improve readability and maintainability.

Consider refactoring the message construction:

+ private func createPresenceMessage(clientID: String, action: PresenceEventType, status: String?) -> String {
+     let clientPresenceChangeMessage = "\(clientID) \(action.displayedText)"
+     return status != nil ? "\(clientPresenceChangeMessage) with status: \(status!)" : clientPresenceChangeMessage
+ }

  for await event in try await room().presence.subscribe(events: [.enter, .leave, .update]) {
      withAnimation {
          let status = event.data?.userCustomData?["status"]?.value as? String
-         let clientPresenceChangeMessage = "\(event.clientID) \(event.action.displayedText)"
-         let presenceMessage = status != nil ? "\(clientPresenceChangeMessage) with status: \(status!)" : clientPresenceChangeMessage
+         let presenceMessage = createPresenceMessage(clientID: event.clientID, action: event.action, status: status)
          messages.insert(BasicListItem(id: UUID().uuidString, title: "System", text: presenceMessage), at: 0)
      }
  }

Also consider separating system messages from chat messages to improve user experience:

Consider creating a separate view model property for system messages and displaying them in a different UI component to avoid mixing them with chat messages.

Sources/AblyChat/DefaultOccupancy.swift (2)

30-31: Use descriptive variable names for better clarity

The variables data1 and data2 are not descriptive, which can make the code less readable. It's recommended to use meaningful names that reflect the data they hold.

Consider renaming them to connections and presenceMembers:

-let data1 = metrics["connections"] as? Int ?? 0
-let data2 = metrics["presenceMembers"] as? Int ?? 0
+let connections = metrics["connections"] as? Int ?? 0
+let presenceMembers = metrics["presenceMembers"] as? Int ?? 0

20-21: Enhance comments for maintainability

The comments reference specification codes like (CHA-04a) and (CHA-04c). To improve code readability and maintainability, consider adding brief descriptions or linking to the relevant documentation.

Sources/AblyChat/Presence.swift (1)

111-111: Replace print statement with appropriate logging mechanism

Using print statements for logging in production code is not recommended. It can clutter the console and is not thread-safe. Consider using a logging framework or mechanism appropriate for the project's environment to handle unexpected presence actions.

Replace the print statement with appropriate logging:

@unknown default:
    self = .unknown
-   print("Unknown presence action encountered: \(action)")
+   // Replace with appropriate logging method
+   Logger.log("Unknown presence action encountered: \(action)", level: .warning)

Ensure that you have a Logger utility or use an existing logging framework to handle logging.

Sources/AblyChat/Room.swift (4)

127-151: Refactor createFeatureChannels for Improved Clarity

The method createFeatureChannels contains nested conditionals that can be simplified for better readability and maintainability.

Refactor the method to reduce complexity:

private static func createFeatureChannels(roomID: String, roomOptions: RoomOptions, realtime: RealtimeClient) -> [RoomFeature: DefaultFeatureChannel] {
    var channels: [RoomFeature: DefaultFeatureChannel] = [:]

    for feature in [RoomFeature.messages, .reactions, .presence, .occupancy] {
        let channelOptions = ARTRealtimeChannelOptions()

        if feature == .presence, let presenceOptions = roomOptions.presence {
            if presenceOptions.enter {
                channelOptions.modes.insert(.presence)
            }
            if presenceOptions.subscribe {
                channelOptions.modes.insert(.presenceSubscribe)
            }
        }

        if feature == .occupancy {
            channelOptions.params = ["occupancy": "metrics"]
        }

        let channel = realtime.getChannel(feature.channelNameForRoomID(roomID), opts: channelOptions)
        let contributor = DefaultRoomLifecycleContributor(channel: .init(underlyingChannel: channel), feature: feature)
        channels[feature] = .init(channel: channel, contributor: contributor)
    }

    return channels
}

160-163: Enhance Error Message for Disabled Presence

The fatalError message for the presence property might not provide sufficient context in release builds.

Consider using a custom error or assertion that includes more diagnostic information to aid debugging.


178-181: Enhance Error Message for Disabled Occupancy

Similarly, improve the fatalError message for the occupancy property to provide clearer context.

Providing detailed error messages can help developers quickly identify configuration issues.


112-124: Ensure Consistent Initialization of Features

The initialization of _presence and _occupancy differs in structure. Align their initialization patterns for consistency and readability.

Consider refactoring _occupancy to match the async initialization pattern of _presence:

-_occupancy = options.occupancy != nil ? DefaultOccupancy(
-    featureChannel: featureChannels[.occupancy]!,
-    chatAPI: chatAPI,
-    roomID: roomID,
-    logger: logger
-) : nil
+_occupancy = options.occupancy != nil ? await DefaultOccupancy(
+    featureChannel: featureChannels[.occupancy]!,
+    chatAPI: chatAPI,
+    roomID: roomID,
+    logger: logger
+) : nil
Sources/AblyChat/DefaultPresence.swift (1)

145-146: Remove redundant print statement in decodePresenceData(from:)

The print statement is redundant since the error is already being logged using logger.log. Consider removing the print statement to avoid duplicate logging.

Apply this diff:

-            print("Failed to decode PresenceData: \(error)")
             logger.log(message: "Failed to decode PresenceData: \(error)", level: .error)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 609ea6d and e557487.

📒 Files selected for processing (15)
  • AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved (1 hunks)
  • Example/AblyChatExample/ContentView.swift (3 hunks)
  • Example/AblyChatExample/Mocks/MockClients.swift (3 hunks)
  • Example/AblyChatExample/Mocks/MockRealtime.swift (1 hunks)
  • Package.resolved (1 hunks)
  • Sources/AblyChat/DefaultOccupancy.swift (1 hunks)
  • Sources/AblyChat/DefaultPresence.swift (1 hunks)
  • Sources/AblyChat/Dependencies.swift (1 hunks)
  • Sources/AblyChat/Occupancy.swift (1 hunks)
  • Sources/AblyChat/Presence.swift (4 hunks)
  • Sources/AblyChat/Room.swift (4 hunks)
  • Sources/AblyChat/RoomFeature.swift (1 hunks)
  • Sources/AblyChat/RoomOptions.swift (1 hunks)
  • Tests/AblyChatTests/IntegrationTests.swift (2 hunks)
  • Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • Package.resolved
  • Sources/AblyChat/RoomOptions.swift
🧰 Additional context used
📓 Learnings (2)
Example/AblyChatExample/ContentView.swift (2)
Learnt from: maratal
PR: ably-labs/ably-chat-swift#34
File: Example/AblyChatExample/ContentView.swift:27-33
Timestamp: 2024-11-12T15:07:31.866Z
Learning: In `ContentView.swift`, the `room()` method does not use force try; it uses `try await` and handles errors appropriately.
Learnt from: maratal
PR: ably-labs/ably-chat-swift#34
File: Example/AblyChatExample/ContentView.swift:27-33
Timestamp: 2024-11-12T15:07:39.465Z
Learning: In `ContentView.swift`, the `room()` method does not use force try; it uses `try await` and handles errors appropriately.
Sources/AblyChat/Dependencies.swift (2)
Learnt from: umair-ably
PR: ably-labs/ably-chat-swift#45
File: Sources/AblyChat/Dependencies.swift:25-42
Timestamp: 2024-11-12T15:07:31.865Z
Learning: In the `AblyChat` project, the variable `defaultChannelOptions` is defined elsewhere and is marked with a TODO for future rework.
Learnt from: umair-ably
PR: ably-labs/ably-chat-swift#45
File: Sources/AblyChat/Dependencies.swift:25-42
Timestamp: 2024-11-12T15:07:31.866Z
Learning: In the `AblyChat` project, the variable `defaultChannelOptions` is defined elsewhere and is marked with a TODO for future rework.
🔇 Additional comments (22)
Sources/AblyChat/Occupancy.swift (2)

9-9: LGTM! Good documentation practice.

The link to the specification provides valuable context for the implementation and helps maintain consistency with the Ably SDK requirements.


Line range hint 10-17: LGTM! Well-structured data model.

The OccupancyEvent struct is well-designed with:

  • Appropriate protocol conformance (Sendable, Encodable, Decodable)
  • Clear public interface
  • Immutable properties initialized through constructor

Let's verify the struct matches the specification exactly:

✅ Verification successful

Let me gather more information about the specification to verify the struct matches exactly.


The OccupancyEvent struct implementation is correct and matches the specification

The code shows that:

  • The struct is properly documented with spec reference (CHA-O2) in Sources/AblyChat/Occupancy.swift
  • It's correctly used in both REST API calls (getOccupancy) and realtime subscriptions as per (CHA-O3) and (CHA-04a)
  • The properties connections and presenceMembers match the expected format and are used consistently throughout the codebase
  • Integration tests verify the expected behavior with real data
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the OccupancyEvent struct matches the specification
# Expected: The struct should match the occupancy event format in the specification

# Fetch the specification and check the occupancy event format
curl -s "https://sdk.ably.com/builds/ably/specification/main/chat-features/#chat-structs-occupancy-event" | grep -A 5 "occupancy event format"

# Check if this struct is used correctly in the codebase
ast-grep --pattern 'OccupancyEvent' -A 3

Length of output: 2146


Script:

#!/bin/bash
# Search for any documentation or comments about the OccupancyEvent format
rg -i "occupancy.*event" --type md
rg -i "occupancy.*event" --type swift -A 3

# Look for any test files that might contain expected format
fd "test.*swift" --exec grep -l "OccupancyEvent" {} \;

Length of output: 6971

Sources/AblyChat/Dependencies.swift (1)

27-31: ⚠️ Potential issue

Verify complete transfer of defaultChannelOptions properties

The current implementation only merges the params dictionary from defaultChannelOptions. Other properties from defaultChannelOptions might be lost in this new implementation.

Let's verify what properties are available in defaultChannelOptions:

Consider applying this fix to preserve all properties:

-        let resolvedOptions = ARTRealtimeChannelOptions()
+        // Start with defaultChannelOptions as the base
+        let resolvedOptions = defaultChannelOptions.copy() as! ARTRealtimeChannelOptions
 
         // Merge params if available, using opts first, then defaultChannelOptions as fallback
         resolvedOptions.params = (opts?.params ?? [:]).merging(
             defaultChannelOptions.params ?? [:]
         ) { _, new in new }
Sources/AblyChat/RoomFeature.swift (2)

17-21: LGTM! Well-documented channel naming conventions.

The grouping of .messages, .presence, and .occupancy cases with shared channel suffix is well-documented with clear references to specification points (CHA-M1, CHA-PR1, CHA-O1). The comments provide excellent clarity on the channel naming format.


17-21: Consider the implications of channel sharing.

While grouping messages, presence, and occupancy on the same channel (chatMessages) can reduce complexity and network overhead, it's worth considering:

  1. Whether this might impact scalability if message volume becomes high
  2. If there are any potential race conditions or ordering requirements between these different types of events
  3. How error handling and recovery strategies might differ for these different concerns

Let's check if there are any relevant integration tests covering this shared channel behavior:

✅ Verification successful

Let's examine the integration tests to understand how the shared channel behavior is implemented and tested.


Let's search for more details about how the room and channel interactions are tested, particularly focusing on presence and message handling.


Let's check for any concurrent message, presence, and occupancy handling in the tests to understand how the shared channel behavior is tested.


Let's try a broader search to understand how messages and events are handled in the tests.


Based on the comprehensive integration tests, I can now provide a final response about the channel sharing concerns:

Channel sharing implementation is well-tested and properly handled

The integration tests demonstrate that the shared channel design is robust and well-implemented:

  • Messages, presence, and occupancy events are handled independently with separate subscriptions and clear event segregation
  • Tests verify concurrent operations without interference:
    • Message publishing and receiving (steps 6-9)
    • Presence state changes with enter/leave/update (steps 17-23)
    • Occupancy updates tracking both connections and presence members (steps 12-16)
  • The implementation includes proper timing controls to handle race conditions
  • Error scenarios are covered through detach/release operations (steps 24-28)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for integration tests related to channel sharing
rg -l "basicIntegrationTest|channelNameForRoomID" Tests/

Length of output: 101


Script:

#!/bin/bash
# Look at the integration tests content
rg -A 10 -B 10 "basicIntegrationTest|channelNameForRoomID" Tests/AblyChatTests/IntegrationTests.swift

Length of output: 825


Script:

#!/bin/bash
# Look for room and channel interaction tests
rg -A 20 "roomID = \"basketball\"" Tests/AblyChatTests/IntegrationTests.swift

Length of output: 723


Script:

#!/bin/bash
# Search for message, presence, and occupancy handling in the integration tests
rg -A 30 "onMessage|onPresenceChange|onOccupancyChange" Tests/AblyChatTests/IntegrationTests.swift

Length of output: 100


Script:

#!/bin/bash
# Search for any message or event handling patterns in the tests
rg -A 30 "subscribe|publish|enter|leave|update" Tests/AblyChatTests/IntegrationTests.swift

Length of output: 8177

Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (1)

5-7: ⚠️ Potential issue

Implement presence mock for testing presence functionality

Since this PR focuses on Presence + Occupancy implementation, the presence property should be properly implemented instead of triggering fatalError. This is crucial for testing the new presence-related features.

Let's verify the usage of this mock in tests:

Would you like me to provide a sample implementation of the presence mock that aligns with the PR objectives?

Example/AblyChatExample/Mocks/MockRealtime.swift (2)

39-41: LGTM! Implementation matches mock pattern.

The presence property implementation follows the consistent pattern used throughout this mock class, throwing fatalError for unimplemented functionality. This is appropriate for a mock implementation that will be replaced later.


39-41: Verify tracking of presence implementation.

While the fatalError implementation is appropriate for now, ensure there's a tracking issue for implementing proper presence functionality in this mock, as it will be needed for testing the presence and occupancy features being developed.

Tests/AblyChatTests/IntegrationTests.swift (2)

35-50: LGTM: Well-structured room initialization with feature options

The initialization of rooms with presence, reactions, and occupancy options is consistent between both tx and rx clients, which is a good practice for integration testing.


175-191: LGTM: Proper cleanup and verification

The detach and release process is well-implemented with appropriate status change verification and object identity checks.

Example/AblyChatExample/Mocks/MockClients.swift (3)

280-280: LGTM: Improved type safety with PresenceData

The change from using a dictionary to a structured PresenceData type improves type safety and aligns with the protocol streamlining objective.

Also applies to: 292-292


Line range hint 304-314: LGTM: Consistent presence data handling

The changes properly implement the optional PresenceData parameter across all presence methods (enter, update, leave) with appropriate default values. The update method now correctly uses .update action instead of .leave.

Also applies to: 317-327, 330-340


288-288: Verify consistency with real implementation

The parameter change from optional to required is a breaking change. Let's verify this matches the real implementation.

✅ Verification successful

Parameter is correctly required in the protocol definition

The get(params:) method in the mock implementation correctly matches the protocol definition in Sources/AblyChat/Presence.swift where the parameter is required, not optional. The protocol defines two distinct methods:

  • func get() async throws -> [PresenceMember]
  • func get(params: PresenceQuery) async throws -> [PresenceMember]

The implementation in the mock client is consistent with both the protocol and the real implementation in DefaultPresence.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the real Presence protocol definition and its implementations
ast-grep --pattern 'protocol Presence {
  $$$
  func get(params: PresenceQuery) $$$
  $$$
}'

Length of output: 94


Script:

#!/bin/bash
# Let's try a broader search for Presence protocol and its method definitions
rg "protocol Presence" -A 10

# Also search for any get method with PresenceQuery parameter
ast-grep --pattern 'func get(params: PresenceQuery)'

# And search for actual implementations
rg "func get.*PresenceQuery" -A 2

Length of output: 1976

Example/AblyChatExample/ContentView.swift (2)

137-138: LGTM: Proper integration of presence and occupancy tasks

The new tasks are appropriately placed and follow the established error handling pattern using tryTask.


53-60: LGTM: Room initialization properly configured for presence and occupancy

The initialization of room options with empty instances for presence, reactions, and occupancy aligns well with the PR objectives.

Let's verify the room options are properly defined:

✅ Verification successful

Based on the search results, I can now provide a definitive response about the room options verification:

Room initialization with presence, reactions, and occupancy options is correctly implemented

The codebase shows proper support for these options:

  • RoomOptions struct in Sources/AblyChat/RoomOptions.swift correctly defines all required fields:
    • presence: PresenceOptions?
    • reactions: RoomReactionsOptions?
    • occupancy: OccupancyOptions?
  • Empty initialization is valid as these are optional properties with proper default values
  • The implementation follows the specification requirements (CHA-PR9)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for room options structure definition
ast-grep --pattern 'struct $ROOM_OPTIONS {
  $$$
}'

Length of output: 53


Script:

#!/bin/bash
# Let's try a broader search for room-related options or configurations
rg -i "room.*options" -A 5 -B 5

Length of output: 44497

Sources/AblyChat/DefaultOccupancy.swift (1)

47-49: Verify access control levels for consistency

The method subscribeToDiscontinuities() is marked as internal. Ensure that this access level aligns with how the method will be used elsewhere in the codebase. If it needs to be accessible outside the module, consider marking it as public.

Please review all method access levels to confirm they match the intended visibility.

Sources/AblyChat/Presence.swift (1)

81-83: Ensure nil data is properly handled in presence methods

The methods enter(data:), update(data:), and leave(data:) now accept optional PresenceData?. Ensure that the implementations of these methods correctly handle nil data and that passing nil aligns with the intended behavior of the presence system.

Run the following script to check for method implementations handling nil data:

✅ Verification successful

nil data is properly handled in presence methods

After reviewing the implementation in DefaultPresence.swift, I can confirm that nil data is properly handled:

  1. All presence methods (enter, update, leave) have default parameter values of nil
  2. The data is safely passed through using optional chaining: data?.asQueryItems()
  3. When decoding presence data, nil is properly handled in decodePresenceData
  4. The PresenceData structure itself is designed to handle optional userCustomData
  5. When nil or empty data is provided, it's converted to an empty string in asQueryItems() method
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for implementations of presence methods and check for handling of nil data.

# Find all implementations of Presence protocol methods
rg -A 5 'func enter\(data: PresenceData\?\)' --no-heading

# Check for handling of nil data within these methods
rg -A 10 'func enter\(data: PresenceData\?\)' --no-heading | rg 'if.*data == nil' -B 2 -A 2

Length of output: 662


Script:

#!/bin/bash
# Let's search for the actual implementations of these presence methods
# First, find files that might contain the implementations
fd -e swift | xargs rg -l "class.*:.*Presence"

# Then look for the actual implementations
rg -A 10 "func (enter|update|leave)\(data: PresenceData\?\)" --no-heading

# Also search for any test files that might show usage patterns
fd -e swift | xargs rg -l "TestCase.*Presence"

Length of output: 1101


Script:

#!/bin/bash
# Let's check the DefaultPresence implementation
cat Sources/AblyChat/DefaultPresence.swift

# Also search for any usage of PresenceData to understand the data structure
rg "PresenceData" -A 5 -B 5

Length of output: 26208

Sources/AblyChat/Room.swift (1)

87-90: Verify All Required Features are Properly Initialized

Ensure that all feature channels are initialized when their corresponding options are enabled, and handle cases where they might be missing to prevent crashes.

Run the following script to check for any uninitialized feature channels:

✅ Verification successful

Let me gather more information about how these feature channels are created and initialized.


Let me gather one final piece of information about the RoomFeature enum to understand all possible features.


Feature Channel Initialization is Properly Handled

The code is correctly handling feature channel initialization. Here's why:

  1. All required features (messages, reactions, presence, occupancy) are explicitly initialized in createFeatureChannels.
  2. The force unwrapping of feature channels is safe because:
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all enabled features have corresponding channels initialized.

# Search for feature channel initializations
rg 'featureChannels\[\.\w+\]!\s*' -A 5

Length of output: 1366


Script:

#!/bin/bash
# Search for feature channel creation and initialization
rg "createFeatureChannels" -A 10

# Search for feature channel options
rg "RoomOptions" -A 5

Length of output: 16561


Script:

#!/bin/bash
# Search for RoomFeature enum definition
rg "enum RoomFeature" -A 10

Length of output: 659

Sources/AblyChat/DefaultPresence.swift (4)

35-42: Ensure continuation is properly resumed in get(params:) method

Similar to the get() method, the get(params:) method needs to ensure the continuation is resumed in all code paths. Remove the unnecessary Task and handle errors appropriately.

Apply this diff:

 return try await withCheckedThrowingContinuation { continuation in
     channel.presence.get(params.asARTRealtimePresenceQuery()) { [processPresenceGet] members, error in
-        Task {
             do {
                 try processPresenceGet(continuation, members, error)
+                // Continuation is resumed inside processPresenceGet
             } catch {
+                continuation.resume(throwing: error)
             }
-        }
     }
 }

75-85: Await asynchronous call and handle errors in update(data:) method

Similarly to enter, the update method should await the completion and handle errors using withCheckedThrowingContinuation.

Apply this diff:

- channel.presence.update(data?.asQueryItems()) { [logger] error in
-     Task {
-         if let error {
-             logger.log(message: "Error updating presence: \(error)", level: .error)
-             throw error
-         }
-     }
- }
+ try await withCheckedThrowingContinuation { continuation in
+     channel.presence.update(data?.asQueryItems()) { [logger] error in
+         if let error {
+             logger.log(message: "Error updating presence: \(error)", level: .error)
+             continuation.resume(throwing: error)
+         } else {
+             continuation.resume()
+         }
+     }
+ }

88-98: Await asynchronous call and handle errors in leave(data:) method

The leave method should also use withCheckedThrowingContinuation to properly await the asynchronous operation and handle any errors.

Apply this diff:

- channel.presence.leave(data?.asQueryItems()) { [logger] error in
-     Task {
-         if let error {
-             logger.log(message: "Error leaving presence: \(error)", level: .error)
-             throw error
-         }
-     }
- }
+ try await withCheckedThrowingContinuation { continuation in
+     channel.presence.leave(data?.asQueryItems()) { [logger] error in
+         if let error {
+             logger.log(message: "Error leaving presence: \(error)", level: .error)
+             continuation.resume(throwing: error)
+         } else {
+             continuation.resume()
+         }
+     }
+ }

115-128: Handle errors in subscribe(events:) method's closure

The same issue exists in the subscribe(events:) method. Ensure that errors thrown by processPresenceSubscribe are caught and handled to prevent unhandled exceptions.

Apply this diff:

 channel.presence.subscribe(event.toARTPresenceAction()) { [processPresenceSubscribe, logger] message in
     logger.log(message: "Received presence message: \(message)", level: .debug)
     Task {
-        let presenceEvent = try processPresenceSubscribe(message, event)
-        subscription.emit(presenceEvent)
+        do {
+            let presenceEvent = try processPresenceSubscribe(message, event)
+            subscription.emit(presenceEvent)
+        } catch {
+            logger.log(message: "Error processing presence message: \(error)", level: .error)
+            // Handle error appropriately
+        }
     }
 }

Tests/AblyChatTests/IntegrationTests.swift Show resolved Hide resolved
Example/AblyChatExample/ContentView.swift Show resolved Hide resolved
Sources/AblyChat/DefaultOccupancy.swift Outdated Show resolved Hide resolved
Sources/AblyChat/DefaultOccupancy.swift Outdated Show resolved Hide resolved
Sources/AblyChat/Presence.swift Show resolved Hide resolved
Sources/AblyChat/Room.swift Show resolved Hide resolved
Sources/AblyChat/DefaultPresence.swift Outdated Show resolved Hide resolved
Sources/AblyChat/DefaultPresence.swift Outdated Show resolved Hide resolved
Sources/AblyChat/DefaultPresence.swift Outdated Show resolved Hide resolved
Sources/AblyChat/DefaultPresence.swift Outdated Show resolved Hide resolved
Copy link

coderabbitai bot commented Nov 18, 2024

✅ Actions performed

Reviews paused.

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.

1 participant