Skip to content

Conversation

@nuno-vieira
Copy link
Member

@nuno-vieira nuno-vieira commented Oct 16, 2025

🔗 Issue Links

https://linear.app/stream/issue/IOS-1152/message-delivery-status

🎯 Goal

Add information to the message whenever it has been delivered to the recipient.

📝 Summary

New Public API:

  • Adds ChatRemoteNotificationHandler.markMessageAsDelivered(deliveries:)
  • Adds ChatChannel.reads(message:) and ChatChannel.deliveredReads(message:)
  • Adds ChatChannelRead.lastDeliveredAt
  • Adds ChatChannelRead.lastDeliveredMessageId

New in UI SDK:

  • Adds support for double grey checkmark when message is delivered but not read

Demo App:

  • Updates the Demo App to show off the delivery feature:
    • Adds gray double checkmark to the message view when message is delivered but not read
    • Adds gray double checkmark to the channel item view when preview message is delivered but not read
    • Adds "Show messages reads info" action to the message to show all delivered and reads info

TODO:

  • Backend config flag to feature flag the feature
  • Update the NotificationService to not use the Tracker internally since it is useless
  • User Privacy Settings update

🛠 Implementation

This PR introduces a message delivery tracking system that automatically marks messages as delivered when they are received through WebSocket events, channel list synchronisation, or push notifications. The system includes throttling to prevent server spam and ensures efficient delivery status updates.

The message delivery tracking consists of four components that work together:

  1. ChannelDeliveryTracker (New) - Core component that manages pending deliveries and throttling.
  2. ChannelDeliveredMiddleware (New) - WebSocket event middleware that uses the ChannelDeliveryTracker to respond to the following events: message.new, message.delivered, message.read
  3. ChannelListController - Marks channels as delivered without throttling when fetching channels.
  4. ChatRemoteNotificationHandler - Handles delivery marking for push notifications, which internally uses the ChannelDeliveryTracker to make sure requests are throttled as well.
  5. MessageDeliveryCriteriaValidator: Responsible for validating if a message can be marked as delivered.

Tip

Debouncer vs Throttling vs Throttling-Trailing
If you are confused about these, here is a visual demo:
https://claude.ai/public/artifacts/8e0e8f6a-674e-4154-9b97-85a99f4956d0

ChannelDeliveryTracker

The ChannelDeliveryTracker is the central component that manages pending channel deliveries and implements throttling to prevent server spam.

Key Features:

  • Tracks channels that need delivery marking with their latest message IDs ([ChannelId: MessageId])
  • Uses a throttler to limit delivery requests to at most one per second. It uses broadcastLatest: true to make sure the last call is always executed.
  • Whenever a delivery call is executed, it only removes the channels from the pending delivery dictionary that still match the same message ID, so that new message IDs from the same channel are not discarded.
  • Thread Safety: All operations are performed on a concurrent queue with barrier flags

Rules to mark a message as delivered:

  • Must be from another user
  • Message hasn't been marked as delivered yet (createdAt > lastDeliveryAt)
  • Message hasn't been marked as read yet (createdAt > lastReadAt)

ChannelDeliveredMiddleware

The ChannelDeliveredMiddleware processes WebSocket events and automatically triggers delivery marking for new messages.

Event Handling:

  • MessageNewEvent: Adds channel to pending deliveries and executes the request if not throttled.
  • NotificationMarkReadEvent: Cancels pending deliveries for the given channel.
  • MessageDeliveredEvent: Updates local channel read data with delivery data. Useful to update the users who received the message.

ChannelListController

The ChannelListController automatically marks channels as delivered during synchronisation operations.

Integration Points:

  • Initial Sync: When synchronize() is called, channels are marked as delivered instantly after a successful fetch.
  • Load Next Channels: When loadNextChannels() is called, new channels are marked as delivered.

Note: Only the channels that respect the rules mentioned above are marked as delivered.

ChatRemoteNotificationHandler

The ChatRemoteNotificationHandler exposes an easy way to mark messages as delivered when they arrive via push notifications. It internally checks if the message was already delivered to avoid redundant API calls. If the App is active, once the push notification is handled, the message will already be marked as delivered.

Customers just need to call the function ChatRemoteNotificationHandler.markMessageAsDelivered() and everything will be handled automatically for them.

Note

The ChatRemoteNotificationHandler does not use the ChannelDeliveryTracker internally since the NotificationService is spawned for every push, which means we can't have a throttling mechanism here since everything is created from scratch.

Full flow with edge cases

1. New message from Channel A is added to pending delivery
2. Request is fired instantly since no throttling is happening
3. New message from Channel B is added before 1s (Request not fired)
4. New message from Channel C is added before 1s (Request not fired)
6. After 1s, Request from 4. is executed with Channel B & Channel C
7. While it is being executed, a new message from the same channel B is added
8. Request from 5. finishes and removes Channel C from pending delivery, not Channel B, since it changed the message meanwhile
9. After 1s again, the request from 6. is executed with Channel B, since it was not cleared by 7.

🎨 Showcase

demo.mov

🧪 Manual Testing Notes

Message is marked delivered when the user receives a new message:

  • Open Channel List with User A
  • Send a message with User B
  • User B should see a double grey checkmark on the new message
  • Long-tapping the new message should show the delivered user
  • Going to the Channel List with User B should show a double grey checkmark

Message is marked delivered when the user receives a push notification:

  • Close the app with User A
  • Send a message with User B
  • User A should receive a push notification
  • User B should see a double grey checkmark on the new message
  • Long-tapping the new message should show the delivered user
  • Going to the Channel List with User B should show a double grey checkmark

Message is marked delivered when the user loads the channel list:

  • Turn off push notifications with User A
  • Close the app with User A
  • Send a message with User B
  • User B should NOT see a double grey checkmark on the new message
  • Open the Channel List with User A
  • User B should see a double grey checkmark on the new message
  • Going to the Channel List with User B should show a double grey checkmark

Message is NOT marked delivered when the user is: (Follow mark read rules)

  • The channel has delivery feature disabled.
  • The channel is muted or hidden
  • The current user has delivery receipts disabled (Demo App User Profile View)
  • The message is not from a shadow-banned or muted user
  • The message is a thread reply
  • The message is from the current user

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Documentation has been updated in the docs-content repo

Summary by CodeRabbit

  • New Features

    • Message delivery tracking with per-channel delivered status and visual double-check indicator
    • Delivery receipts privacy setting so users can enable/disable delivery notifications
    • "Message Reads" view showing per-user delivered/read info and a "Show Message Reads" action
    • Channels can opt into delivery event tracking
  • Improvements

    • Message status now distinguishes sent, delivered, and read with updated UI behavior

@nuno-vieira nuno-vieira requested a review from a team as a code owner October 16, 2025 14:45
@nuno-vieira nuno-vieira added the ✅ Feature An issue or PR related to a feature label Oct 16, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 16, 2025

Walkthrough

Implements delivery-receipts: new delivery models, event types and middleware, a throttled ChannelDeliveryTracker, API endpoint/payloads, database fields and Core Data updates, CurrentUserUpdater wiring, validator logic, UI additions (reads view, delivery status), demo app toggles, push integration, and extensive tests/mocks.

Changes

Cohort / File(s) Summary
Core delivery types & validator
Sources/StreamChat/Models/MessageDelivery/DeliveredMessageInfo.swift, Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
New MessageDeliveryInfo model and MessageDeliveryCriteriaValidating + MessageDeliveryCriteriaValidator implementation to decide eligibility for marking delivered.
Delivery tracker
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift
New ChannelDeliveryTracker: thread-safe batching/throttling of per-channel delivery submissions with submitForDelivery/cancel.
Event model & middleware
Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift, .../EventType.swift, .../EventPayload.swift, .../EventMiddlewares/ChannelDeliveredMiddleware.swift
Adds message.delivered event, MessageDeliveredEvent/DTO, extends EventPayload with delivery fields, and new ChannelDeliveredMiddleware handling delivery-related events.
API endpoints & payloads
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift, .../EndpointPath.swift, .../EndpointPath+OfflineRequest.swift, .../Payloads/ChannelDeliveredPayload.swift
New Endpoint path channels/delivered, Endpoint builder markChannelsDelivered(payload:), offline-queue eligibility update, and payload structs DeliveredMessagePayload/ChannelDeliveredRequestPayload.
CurrentUserUpdater & Workers
Sources/StreamChat/Workers/CurrentUserUpdater.swift, Sources/StreamChat/ChatClient+Environment.swift, Sources/StreamChat/ChatClientFactory.swift, Sources/StreamChat/ChatClient.swift
New markMessagesAsDelivered API on CurrentUserUpdater; environment builders and ChatClient wiring for CurrentUserUpdater and ChannelDeliveryTracker; middleware injection.
Channel/controller wiring
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
Adds deliveryCriteriaValidator/currentUserUpdater wiring and markChannelsAsDeliveredIfNeeded invocation after channel updates; environment builders added.
Database & DTOs
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift, .../ChannelConfigDTO.swift, .../CurrentUserDTO.swift, StreamChatModel.xcdatamodeld/contents
New DTO fields: lastDeliveredAt, lastDeliveredMessageId, deliveryEventsEnabled, isDeliveryReceiptsEnabled; mapping and persistence updates.
Models & payload mapping
Sources/StreamChat/Models/Channel.swift, Sources/StreamChat/Models/ChannelRead.swift, Sources/StreamChat/Models/ChatMessage.swift, Sources/StreamChat/Models/MessageDelivery/DeliveredMessageInfo.swift, Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift, Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift
Adds ChatChannel.reads/deliveredReads, ChatChannelRead fields lastDeliveredAt/lastDeliveredMessageId, ChatMessage.deliveryStatus(for:) and new delivered status; payload mapping updated.
WebSocket/client handling
Sources/StreamChat/WebSocketClient/Events/*, Sources/StreamChat/WebSocketClient/WebSocketConnectPayload.swift
Event DTO/class conversions updated; WebSocket connect payload and privacy payloads include deliveryReceipts settings.
UI & Demo app
DemoApp/StreamChat/Components/DeliveredMessages/DemoMessageReadsInfoView.swift, DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift, DemoApp/Screens/AppConfigViewController/*, DemoApp/Screens/UserProfile/UserProfileViewController.swift, Sources/StreamChatUI/...
New DemoMessageReadsInfoView; show message reads action in demo actions; demo config flag isMessageDeliveredInfoEnabled and user deliveryReceiptsEnabled toggle; UI components updated to display delivered state (.delivered mapped to read image).
Push notifications
DemoAppPush/NotificationService.swift, Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
Push handling calls markMessageAsDelivered when appropriate; ChatRemoteNotificationHandler gets currentUserUpdater and deliveryCriteriaValidator and exposes markMessageAsDelivered(message:for:).
Tests, fixtures & mocks
Tests/StreamChatTests/**, TestTools/StreamChatTestTools/Mocks/**, TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Message/MessageDelivered.json
New/updated tests for endpoints, payloads, tracker, middleware, validator, models and UI snapshots; new mocks for ChannelDeliveryTracker, CurrentUserUpdater, MessageDeliveryCriteriaValidator; JSON fixture added.
Project & changelog
StreamChat.xcodeproj/project.pbxproj, CHANGELOG.md, TestMockServer ChannelConfig.swift
Project file additions for new sources/tests, changelog entry, mock server delivery flag added.

Sequence Diagram(s)

sequenceDiagram
    participant C as ChannelListController
    participant V as MessageDeliveryCriteriaValidator
    participant T as ChannelDeliveryTracker
    participant U as CurrentUserUpdater
    participant API as APIClient

    C->>C: detect/update channels
    C->>V: validate per-channel candidate message
    alt eligible
        C->>T: submitForDelivery(channelId, messageId)
    end

    Note right of T: Throttler batches (1s)
    T->>U: markMessagesAsDelivered([MessageDeliveryInfo])
    U->>API: POST /channels/delivered (ChannelDeliveredRequestPayload)
    API-->>U: EmptyResponse
Loading
sequenceDiagram
    participant UI as UI Layer
    participant M as ChatMessage
    participant CH as ChatChannel
    participant DB as Database

    UI->>M: deliveryStatus(for: CH)
    M->>CH: reads(for: message)
    alt has read
        CH-->>M: read -> return .read
    else has delivered
        CH-->>M: deliveredReads -> return .delivered
    else
        M-->>UI: .sent
    end

    Note over DB: message.delivered event -> ChannelDeliveredMiddleware updates ChannelRead and cancels pending tracker entries
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • ChannelDeliveryTracker.swift: concurrency, barrier writes, throttler correctness.
  • ChannelDeliveredMiddleware.swift: event-to-DB mapping and cancellation semantics.
  • MessageDeliveryCriteriaValidator.swift: multiple eligibility conditions and edge cases (thread replies, muted users, timestamps).
  • ChatMessage.deliveryStatus(for:) & UI mappings: precedence (read > delivered) and gating via channel config.
  • Core Data model changes and DTO mappings (migration/consistency).
  • Tests: ensure mocks and new builders are wired correctly in test environments.

Possibly related PRs

Suggested labels

🌐 SDK: StreamChat (LLC), 🎨 SDK: StreamChatUI, 🟢 QAed

Suggested reviewers

  • laevandus
  • martinmitrevski

Poem

🐰 I hopped through code with twitching nose,

Marked messages delivered in neat little rows.
Double checks sparkle, no mystery remains,
Throttled and tested — receipts in my veins. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive While the vast majority of changes are directly related to implementing message delivery functionality, some changes appear tangentially related or could be considered supporting infrastructure. Specifically, changes to CurrentUserUpdater, privacy settings (deliveryReceipts), UserProfileViewController, and CurrentChatUser mock initialization may extend beyond strict delivery status requirements. Additionally, modifications to event types and message event class hierarchies (converting structs to classes) appear broader than necessary for delivery info alone. To clarify scope: verify whether privacy settings enhancements (deliveryReceipts privacy setting), CurrentUserUpdater modifications, and the struct-to-class conversion of message events are essential dependencies for the delivery feature or represent architectural improvements that could be separated into future PRs. If these are necessary for the feature to function, document the architectural rationale.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'Add support for message delivered info' is clear, concise, and directly describes the main change in the changeset. It accurately represents the primary objective of implementing message delivery status functionality, including APIs, UI updates, and delivery tracking mechanisms. The title is not vague or misleading.
Linked Issues check ✅ Passed The pull request comprehensively implements all coding-related requirements from IOS-1152. It includes the new public APIs (ChatRemoteNotificationHandler.markMessageAsDelivered, ChatChannel.reads/deliveredReads, ChatChannelRead.lastDeliveredAt/lastDeliveredMessageId), automatic delivery marking via WebSocket events and channel list synchronization, throttling mechanisms via ChannelDeliveryTracker, delivery criteria validation, support for push notifications, and UI components to display delivery status with double grey checkmarks.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add/message-delivered-status

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.

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: 7

🧹 Nitpick comments (15)
DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (1)

12-14: LGTM!

Consolidating AppConfig.shared.demoAppConfig access into a computed property is a good refactor. This reduces duplication and improves testability.

DemoApp/StreamChat/Components/DeliveredMessages/DemoMessageReadsInfoView.swift (3)

119-126: Differentiate delivered and read icons.

Both .delivered and .read cases return the same "checkmark" icon, making it difficult for users to distinguish between delivered and read status at a glance.

Apply this diff to use distinct icons:

 var icon: String {
     switch self {
     case .delivered:
-        return "checkmark"
+        return "checkmark.circle"
     case .read:
-        return "checkmark"
+        return "checkmark.circle.fill"
     }
 }

146-153: Improve fallback text for missing user names.

Line 149 falls back to user.id when user.name is nil. Displaying technical user IDs as avatar initials degrades the user experience.

Apply this diff to use a more user-friendly fallback:

 Circle()
     .fill(Color.gray.opacity(0.3))
     .overlay(
-        Text(user.name?.prefix(1).uppercased() ?? user.id)
+        Text(user.name?.prefix(1).uppercased() ?? "?")
             .font(.headline)
             .foregroundColor(.primary)
     )

95-105: Consider consolidating helper methods.

getDeliveredTimestamp and getReadTimestamp share nearly identical logic, differing only in the keypath accessed (.lastDeliveredAt vs .lastReadAt).

You could refactor using a generic helper:

private func getTimestamp(for user: ChatUser, keyPath: KeyPath<ChatChannelRead, Date?>) -> Date? {
    channel.reads
        .first { $0.user.id == user.id }
        .flatMap { $0[keyPath: keyPath] }
}

private func getDeliveredTimestamp(for user: ChatUser) -> Date? {
    getTimestamp(for: user, keyPath: \.lastDeliveredAt)
}

private func getReadTimestamp(for user: ChatUser) -> Date? {
    getTimestamp(for: user, keyPath: \.lastReadAt)
}

However, given the simplicity of the existing methods, this refactor is optional.

Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)

283-294: Consider adding empty array check and async variant.

While the implementation is correct, consider these optional improvements:

  1. Empty array optimization: Add an early return if messages.isEmpty to avoid unnecessary API calls:

    guard !messages.isEmpty else {
        completion?(nil)
        return
    }
  2. Async/await variant: For consistency with other methods in this class (see lines 298-376), consider adding an async version in the extension below.

Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)

579-600: New public API looks correct; consider no-op on empty input

Validation and delegation are consistent with existing patterns. Optionally, early-return on empty messages to avoid a needless request.

 func markMessagesAsDelivered(
     _ messages: [DeliveredMessageInfo],
     completion: ((Error?) -> Void)? = nil
 ) {
+    guard !messages.isEmpty else {
+        completion?(nil)
+        return
+    }
     guard client.currentUserId != nil else {
         completion?(ClientError.CurrentUserDoesNotExist())
         return
     }
Sources/StreamChat/ChatClientFactory.swift (1)

116-119: Remove or use the unused currentUserId parameter; fix call site closure

currentUserId isn’t used here, and the call site passes { nil }. Either:

  • Remove the parameter from the factory and update call sites, or
  • Use the provided closure where needed.

Recommend removing to keep the API clean.

-    func makeEventNotificationCenter(
-        databaseContainer: DatabaseContainer,
-        currentUserId: @escaping () -> UserId?,
-        currentUserUpdater: CurrentUserUpdater,
-        channelDeliveryTracker: ChannelDeliveryTracker
-    ) -> EventNotificationCenter {
+    func makeEventNotificationCenter(
+        databaseContainer: DatabaseContainer,
+        currentUserUpdater: CurrentUserUpdater,
+        channelDeliveryTracker: ChannelDeliveryTracker
+    ) -> EventNotificationCenter {

And adjust the call site (see ChatClient.swift suggestion).

Sources/StreamChat/ChatClient.swift (1)

169-175: Pass an actual currentUserId provider instead of { nil }

If the factory continues to accept currentUserId, pass a real provider to prevent future regressions.

-        let eventNotificationCenter = factory.makeEventNotificationCenter(
-            databaseContainer: databaseContainer,
-            currentUserId: {
-                nil
-            },
-            currentUserUpdater: currentUserUpdater,
-            channelDeliveryTracker: channelDeliveryTracker
-        )
+        let eventNotificationCenter = factory.makeEventNotificationCenter(
+            databaseContainer: databaseContainer,
+            currentUserUpdater: currentUserUpdater,
+            channelDeliveryTracker: channelDeliveryTracker
+        )

If you keep the parameter, use:

-            currentUserId: {
-                nil
-            },
+            currentUserId: { [weak self] in self?.currentUserId },

Follow up with the matching factory signature change (see ChatClientFactory.swift). [Based on learnings]

Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (1)

991-1048: Good coverage for delivered-messages flow

API call shape and completion propagation are well tested. Consider renaming the MARK from “Mark Channels Delivered” to “Mark Messages Delivered” for consistency.

Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift (3)

21-33: Handle NotificationMessageNew and Mark-All-Read events

To cover push-style events and global read, extend the switch:

  • Submit deliveries on NotificationMessageNewEventDTO for messages not from current user.
  • Optionally clear pending on NotificationMarkAllReadEventDTO.

Apply this diff to add NotificationMessageNew handling:

 case let messageNewEvent as MessageNewEventDTO:
   if session.currentUser?.user.id == messageNewEvent.message.user.id {
     break
   }
   handleMessageNewEvent(messageNewEvent)
+case let notificationMessageNewEvent as NotificationMessageNewEventDTO:
+  if session.currentUser?.user.id != notificationMessageNewEvent.message.user.id {
+      deliveryTracker.submitForDelivery(
+          channelId: notificationMessageNewEvent.cid,
+          messageId: notificationMessageNewEvent.message.id
+      )
+  }
 case let notificationMarkReadEvent as NotificationMarkReadEventDTO:
   handleNotificationMarkReadEvent(notificationMarkReadEvent)
+// Optional:
+// case _ as NotificationMarkAllReadEventDTO:
+//   deliveryTracker.cancelAll() // if you add this to the tracker

Please confirm whether a push-notification path already calls ChannelDeliveryTracker; if yes, duplicating here may be redundant.


39-41: Skip ephemeral/system messages (optional)

Avoid submitting delivery for non-regular message types (ephemeral/system/error).

Example:

guard event.message.type == .regular else { return }
deliveryTracker.submitForDelivery(channelId: event.cid, messageId: event.message.id)

55-67: Make deliveredAt monotonic

Out-of-order events can regress deliveredAt. Update only if the incoming timestamp is newer or equal.

-// Update the delivered message information
-channelRead.lastDeliveredAt = event.lastDeliveredAt.bridgeDate
-channelRead.lastDeliveredMessageId = event.lastDeliveredMessageId
+// Update delivered info only if newer
+let newDeliveredAt = event.lastDeliveredAt.bridgeDate
+if let current = channelRead.lastDeliveredAt {
+    if let newDeliveredAt, newDeliveredAt >= current {
+        channelRead.lastDeliveredAt = newDeliveredAt
+        channelRead.lastDeliveredMessageId = event.lastDeliveredMessageId
+    }
+} else {
+    channelRead.lastDeliveredAt = newDeliveredAt
+    channelRead.lastDeliveredMessageId = event.lastDeliveredMessageId
+}
Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift (2)

114-119: Assert deliveredAt equality, not just non-nil

Strengthen the check to prove correct value persisted.

-let channelRead = channelDTO.reads.first { $0.user.id == userId }
-XCTAssertNotNil(channelRead)
-XCTAssertEqual(channelRead?.lastDeliveredMessageId, messageId)
-XCTAssertNotNil(channelRead?.lastDeliveredAt)
+let channelRead = channelDTO.reads.first { $0.user.id == userId }
+XCTAssertNotNil(channelRead)
+XCTAssertEqual(channelRead?.lastDeliveredMessageId, messageId)
+XCTAssertEqual(channelRead?.lastDeliveredAt, deliveredAt)

As per coding guidelines


36-52: Add coverage for NotificationMessageNewEventDTO

Ensure the middleware submits for delivery on notificationMessageNew as well (if implemented).

+func test_handleNotificationMessageNewEvent_callsSubmitForDelivery() throws {
+    // GIVEN
+    let channelId = ChannelId.unique
+    let messageId = MessageId.unique
+    let event = try createNotificationMessageNewEvent(channelId: channelId, messageId: messageId)
+
+    // WHEN
+    _ = middleware.handle(event: event, session: database.viewContext)
+
+    // THEN
+    XCTAssertEqual(deliveryTracker.submitForDelivery_callCount, 1)
+    XCTAssertEqual(deliveryTracker.submitForDelivery_channelId, channelId)
+    XCTAssertEqual(deliveryTracker.submitForDelivery_messageId, messageId)
+}
+
+private func createNotificationMessageNewEvent(
+    channelId: ChannelId,
+    messageId: MessageId
+) throws -> NotificationMessageNewEventDTO {
+    let user = UserPayload.dummy(userId: .unique)
+    let message = MessagePayload.dummy(messageId: messageId, authorUserId: user.id)
+    let channel = ChannelDetailPayload.dummy(cid: channelId)
+    let payload = EventPayload(
+        eventType: .notificationMessageNew,
+        cid: channelId,
+        user: user,
+        channel: channel,
+        message: message,
+        createdAt: message.createdAt
+    )
+    return try NotificationMessageNewEventDTO(from: payload)
+}

As per coding guidelines

Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (1)

72-103: Ensure throttler triggers trailing executions (pending items remain delivered)

If Throttler.execute is leading-edge only, entries added during an in-flight request may remain pending without a subsequent execute. Ensure trailing-edge behavior or re-invoke markMessagesAsDelivered in the success path when pendingDeliveredChannels is not empty.

Option: in the success block, after clearing, check if pendingDeliveredChannels is non-empty and call markMessagesAsDelivered().

Please verify Throttler’s semantics (leading vs trailing). If leading-only, this change is necessary to avoid stuck pending deliveries.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c0bde8a and 4e19cdf.

📒 Files selected for processing (53)
  • DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift (5 hunks)
  • DemoApp/StreamChat/Components/DeliveredMessages/DemoChatMessageDeliveryStatusView.swift (1 hunks)
  • DemoApp/StreamChat/Components/DeliveredMessages/DemoMessageReadsInfoView.swift (1 hunks)
  • DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift (1 hunks)
  • DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (5 hunks)
  • DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift (1 hunks)
  • DemoAppPush/NotificationService.swift (4 hunks)
  • Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift (2 hunks)
  • Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (2 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelDeliveredPayload.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift (2 hunks)
  • Sources/StreamChat/ChatClient+Environment.swift (1 hunks)
  • Sources/StreamChat/ChatClient.swift (3 hunks)
  • Sources/StreamChat/ChatClientFactory.swift (2 hunks)
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (4 hunks)
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1 hunks)
  • Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (3 hunks)
  • Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (2 hunks)
  • Sources/StreamChat/Models/Channel.swift (1 hunks)
  • Sources/StreamChat/Models/ChannelRead.swift (1 hunks)
  • Sources/StreamChat/Models/DeliveredMessageInfo.swift (1 hunks)
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (1 hunks)
  • Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (1 hunks)
  • Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift (1 hunks)
  • Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (5 hunks)
  • Sources/StreamChat/WebSocketClient/Events/EventType.swift (2 hunks)
  • Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift (1 hunks)
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift (1 hunks)
  • StreamChat.xcodeproj/project.pbxproj (34 hunks)
  • TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Message/MessageDelivered.json (1 hunks)
  • TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (1 hunks)
  • TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (1 hunks)
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (3 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (1 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/ChatChannel.swift (1 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (2 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift (1 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (2 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift (1 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift (2 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift (2 hunks)
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (8 hunks)
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (9 hunks)
  • Tests/StreamChatTests/Models/ChatChannel_Tests.swift (1 hunks)
  • Tests/StreamChatTests/StateLayer/Chat_Tests.swift (3 hunks)
  • Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift (1 hunks)
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift (1 hunks)
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift (1 hunks)
  • Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChat/Models/ChannelRead.swift
  • Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift
  • Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift
  • Sources/StreamChat/Models/Channel.swift
  • TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift
  • DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift
  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/ChatChannel.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelDeliveredPayload.swift
  • Sources/StreamChat/ChatClient+Environment.swift
  • Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
  • Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
  • Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift
  • Tests/StreamChatTests/Models/ChatChannel_Tests.swift
  • Sources/StreamChat/Models/DeliveredMessageInfo.swift
  • Sources/StreamChat/ChatClient.swift
  • DemoAppPush/NotificationService.swift
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift
  • Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift
  • DemoApp/StreamChat/Components/DeliveredMessages/DemoChatMessageDeliveryStatusView.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift
  • Sources/StreamChat/ChatClientFactory.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • Sources/StreamChat/WebSocketClient/Events/EventPayload.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift
  • DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift
  • Tests/StreamChatTests/StateLayer/Chat_Tests.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift
  • DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift
  • Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
  • DemoApp/StreamChat/Components/DeliveredMessages/DemoMessageReadsInfoView.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift
  • Sources/StreamChat/WebSocketClient/Events/EventType.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift
  • Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/Models/ChannelRead.swift
  • Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift
  • Sources/StreamChat/Models/Channel.swift
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelDeliveredPayload.swift
  • Sources/StreamChat/ChatClient+Environment.swift
  • Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
  • Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
  • Sources/StreamChat/Models/DeliveredMessageInfo.swift
  • Sources/StreamChat/ChatClient.swift
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
  • Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift
  • Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift
  • Sources/StreamChat/ChatClientFactory.swift
  • Sources/StreamChat/WebSocketClient/Events/EventPayload.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
  • Sources/StreamChat/WebSocketClient/Events/EventType.swift
  • Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Tests/{StreamChatTests,StreamChatUITests}/**/*.swift: Add or extend tests in the matching module’s Tests folder
Prefer using provided test fakes/mocks in tests when possible

Files:

  • Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift
  • Tests/StreamChatTests/Models/ChatChannel_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift
  • Tests/StreamChatTests/StateLayer/Chat_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
Tests/StreamChatTests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover core models and API surface of StreamChat

Files:

  • Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift
  • Tests/StreamChatTests/Models/ChatChannel_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift
  • Tests/StreamChatTests/StateLayer/Chat_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
🧠 Learnings (3)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift
  • Tests/StreamChatTests/Models/ChatChannel_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift
  • Tests/StreamChatTests/StateLayer/Chat_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Add or extend tests in the matching module’s Tests folder

Applied to files:

  • StreamChat.xcodeproj/project.pbxproj
🧬 Code graph analysis (35)
Sources/StreamChat/Models/ChannelRead.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • lastReadMessageId (2224-2236)
Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift (1)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • lastReadMessageId (2224-2236)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (5)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • markMessagesAsDelivered (586-600)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (1)
  • request (131-144)
Sources/StreamChat/APIClient/APIClient.swift (1)
  • request (80-86)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (1)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (1)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift (1)
TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (1)
  • dummy (11-41)
TestTools/StreamChatTestTools/TestData/DummyData/ChatChannel.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • lastReadMessageId (2224-2236)
TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • lastReadMessageId (2224-2236)
Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelDeliveredPayload.swift (1)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • encode (419-423)
Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift (2)
Sources/StreamChat/Utils/Logger/Logger.swift (2)
  • log (277-314)
  • debug (347-362)
Sources/StreamChat/Models/Channel.swift (1)
  • latestMessageNotMarkedAsDelivered (395-402)
Tests/StreamChatTests/WebSocketClient/Events/MessageEvents_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (4)
  • user (100-102)
  • saveChannel (73-80)
  • saveChannel (394-401)
  • saveUser (82-85)
Sources/StreamChat/Database/DatabaseSession.swift (2)
  • saveChannel (713-716)
  • saveUser (718-721)
Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift (5)
  • toDomainEvent (50-66)
  • toDomainEvent (102-115)
  • toDomainEvent (161-185)
  • toDomainEvent (227-246)
  • toDomainEvent (300-313)
Tests/StreamChatTests/Models/ChatChannel_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (3)
  • mock (10-50)
  • mock (55-71)
  • mock (76-149)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
  • mock (11-104)
  • mock (108-126)
Sources/StreamChat/Models/Channel.swift (2)
  • latestMessageNotMarkedAsDelivered (395-402)
  • readState (390-392)
Sources/StreamChat/ChatClient.swift (1)
Sources/StreamChat/ChatClientFactory.swift (1)
  • makeEventNotificationCenter (114-146)
DemoAppPush/NotificationService.swift (1)
Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift (2)
  • handleNotification (141-148)
  • markMessageAsDelivered (151-166)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (3)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
Sources/StreamChat/StateLayer/ConnectedUser.swift (1)
  • currentUserId (205-208)
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (1)
TestTools/StreamChatTestTools/Extensions/Unique/Date+Unique.swift (3)
  • unique (13-15)
  • unique (18-20)
  • unique (28-30)
Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift (3)
Sources/StreamChat/WebSocketClient/Events/EventType.swift (1)
  • event (186-267)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (2)
  • submitForDelivery (50-60)
  • cancel (65-70)
TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (2)
  • submitForDelivery (32-36)
  • cancel (38-41)
Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (1)
  • asModel (74-74)
Sources/StreamChat/Models/Payload+asModel/MessagePayload+asModel.swift (2)
  • asModel (14-143)
  • asModel (149-159)
DemoApp/StreamChat/Components/DeliveredMessages/DemoChatMessageDeliveryStatusView.swift (1)
DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift (1)
  • updateContent (35-40)
Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (5)
Tests/StreamChatTests/StateLayer/Chat_Tests.swift (1)
  • cleanUp (1875-1881)
TestTools/StreamChatTestTools/Mocks/StreamChat/ChatClient_Mock.swift (4)
  • cleanUp (111-126)
  • mock (138-171)
  • mock (303-317)
  • mock (321-335)
TestTools/StreamChatTestTools/SpyPattern/Spy/ChannelListUpdater_Spy.swift (1)
  • cleanUp (32-44)
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/AuthenticationRepository_Mock.swift (1)
  • setMockToken (127-135)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (1)
  • loadNextChannels (169-191)
Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift (7)
Sources/StreamChat/Database/DTOs/UserDTO.swift (3)
  • user (34-40)
  • user (138-140)
  • asModel (194-194)
Sources/StreamChat/Database/DataStore.swift (2)
  • user (34-36)
  • channel (56-58)
Sources/StreamChat/Database/DTOs/ChannelDTO.swift (2)
  • channel (427-429)
  • asModel (506-508)
Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (2)
  • value (253-259)
  • value (262-272)
Sources/StreamChat/WebSocketClient/Events/UserEvents.swift (3)
  • toDomainEvent (27-34)
  • toDomainEvent (57-64)
  • toDomainEvent (104-114)
Sources/StreamChat/WebSocketClient/Events/NotificationEvents.swift (12)
  • toDomainEvent (40-53)
  • toDomainEvent (81-90)
  • toDomainEvent (155-166)
  • toDomainEvent (192-206)
  • toDomainEvent (229-236)
  • toDomainEvent (273-286)
  • toDomainEvent (320-332)
  • toDomainEvent (355-362)
  • toDomainEvent (396-408)
  • toDomainEvent (445-458)
  • toDomainEvent (495-508)
  • toDomainEvent (536-543)
Sources/StreamChat/Models/Payload+asModel/UserPayload+asModel.swift (1)
  • asModel (10-29)
Sources/StreamChat/ChatClientFactory.swift (1)
Sources/StreamChat/StateLayer/ConnectedUser.swift (1)
  • currentUserId (205-208)
Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (1)
  • cleanUp (44-50)
Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelDeliveredMiddleware.swift (1)
  • handle (19-34)
TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift (1)
  • writeSynchronously (175-182)
DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (1)
Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift (1)
  • messageActions (616-644)
TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • lastReadMessageId (2224-2236)
Tests/StreamChatTests/WebSocketClient/ChannelDeliveryTracker_Tests.swift (2)
TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (3)
  • cleanUp (44-50)
  • submitForDelivery (32-36)
  • cancel (38-41)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (2)
  • submitForDelivery (50-60)
  • cancel (65-70)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (5)
Sources/StreamChat/Utils/Logger/Logger.swift (2)
  • error (395-410)
  • log (277-314)
Sources/StreamChat/Models/Channel.swift (1)
  • latestMessageNotMarkedAsDelivered (395-402)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • markMessagesAsDelivered (586-600)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift (7)
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/AuthenticationRepository_Mock.swift (2)
  • setMockToken (127-135)
  • logOutUser (99-101)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • markMessagesAsDelivered (586-600)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
Sources/StreamChat/Repositories/AuthenticationRepository.swift (1)
  • logOutUser (207-212)
TestTools/StreamChatTestTools/Wait/WaitFor.swift (1)
  • waitFor (32-59)
TestTools/StreamChatTestTools/Assertions/AssertTestQueue.swift (1)
  • AssertTestQueue (12-16)
Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (2)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • markMessagesAsDelivered (586-600)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelDeliveredPayload_Tests.swift (1)
Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelDeliveredPayload.swift (2)
  • encode (17-21)
  • encode (37-40)
TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (1)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (2)
  • submitForDelivery (50-60)
  • cancel (65-70)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (1)
TestTools/StreamChatTestTools/Mocks/StreamChat/WebSocketClient/ChannelDeliveryTracker_Mock.swift (2)
  • submitForDelivery (32-36)
  • cancel (38-41)
Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (5)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • markMessagesAsDelivered (586-600)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
  • markChannelsDelivered (295-303)
TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (1)
  • test_simulateResponse (109-112)

@Stream-SDK-Bot
Copy link
Collaborator

SDK Performance

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 3.34 ms 66.6% 🔼 🟢
Duration 2.6 s 2.54 s 2.31% 🔼 🟢
Hitch time ratio 4 ms per s 1.32 ms per s 67.0% 🔼 🟢
Frame rate 75 fps 78.07 fps 4.09% 🔼 🟢
Number of hitches 1 0.4 60.0% 🔼 🟢

@github-actions
Copy link

github-actions bot commented Oct 16, 2025

1 Warning
⚠️ Big PR

Generated by 🚫 Danger

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4e19cdf and ed69298.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • DemoApp/StreamChat/Components/DemoChatChannelVC.swift (1 hunks)
  • Sources/StreamChat/Models/Channel.swift (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • DemoApp/StreamChat/Components/DemoChatChannelVC.swift
  • Sources/StreamChat/Models/Channel.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/Models/Channel.swift
🧬 Code graph analysis (1)
DemoApp/StreamChat/Components/DemoChatChannelVC.swift (2)
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (1)
  • eventsController (566-594)
DemoApp/StreamChat/Components/DemoChatThreadVC.swift (1)
  • eventsController (75-82)
⏰ 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). (3)
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Build Test App and Frameworks
  • GitHub Check: Metrics
🔇 Additional comments (2)
Sources/StreamChat/Models/Channel.swift (2)

394-412: LGTM! Documentation added as requested.

The comprehensive inline documentation has been added to address the previous review feedback. The method logic correctly implements the delivery tracking rules: it returns delivery information only when a read state exists, a latest message is present, the message is from another user, and the message is newer than both the last read and last delivered timestamps.


387-392: Add inline documentation for the new public method.

The coding guidelines require updating inline documentation comments when altering public API. This new public method lacks documentation explaining its purpose, the userId parameter, and the return value.

As per coding guidelines, apply this diff to add documentation:

+    /// Returns the user's read state for this channel.
+    ///
+    /// - Parameter userId: The ID of the user.
+    /// - Returns: The read state, or `nil` if not found.
     public func readState(for userId: UserId) -> ChatChannelRead? {
         reads.first { $0.user.id == userId }
     }

Likely an incorrect or invalid review comment.

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: 1

🧹 Nitpick comments (6)
Sources/StreamChat/Models/ChannelRead.swift (1)

21-25: Consider documenting nil semantics for the new properties.

The documentation for the new delivery properties could be more complete. For consistency with the existing lastReadMessageId documentation (line 13), consider explaining what nil values indicate (e.g., "never delivered", "delivery tracking unavailable", or "delivery feature not enabled").

Apply this diff to enhance the documentation:

-    /// The last time a message has been delivered to this user.
+    /// The last time a message has been delivered to this user. Nil means no messages have been delivered yet or delivery tracking is unavailable.
     public let lastDeliveredAt: Date?
 
-    /// The last message ID that has been delivered to this user.
+    /// The last message ID that has been delivered to this user. Nil means no messages have been delivered yet or delivery tracking is unavailable.
     public let lastDeliveredMessageId: MessageId?
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (5)

31-33: Include deliveryEventsEnabled in the documented channel criterion.

canBeMarkedAsDelivered also checks config.deliveryEventsEnabled, but the doc bullet only mentions mute/hidden. Add it for clarity.

Doc tweak:

-/// - The channel can be marked as delivered (not muted, not hidden)
+/// - The channel can be marked as delivered (`config.deliveryEventsEnabled` is true, not muted, not hidden)

Also applies to: 85-88


72-74: Avoid intermediate array from map in mute check.

Use contains(where:) to skip allocation and be clearer.

-        if currentUser.mutedUsers.map(\.id).contains(message.author.id) {
+        if currentUser.mutedUsers.contains(where: { $0.id == message.author.id }) {
             return false
         }

76-79: Add parentheses to clarify ?? precedence.

Parentheses avoid precedence ambiguity and improve readability.

-            return message.createdAt > userRead.lastReadAt
-                && message.createdAt > userRead.lastDeliveredAt ?? .distantPast
+            return message.createdAt > userRead.lastReadAt
+                && message.createdAt > (userRead.lastDeliveredAt ?? .distantPast)

85-89: Make the computed property explicitly private.

Top-level private extension semantics can be confusing. Make the member’s access explicit.

-private extension ChatChannel {
-    var canBeMarkedAsDelivered: Bool {
+private extension ChatChannel {
+    private var canBeMarkedAsDelivered: Bool {
         config.deliveryEventsEnabled && !isMuted && !isHidden
     }
 }

46-82: Boundary tests: equality and missing read/delivery timestamps.

Please ensure tests cover:

  • createdAt equal to lastReadAt and lastDeliveredAt (should not deliver with strict >).
  • missing read state (current behavior returns true).
  • thread replies with/without showReplyInChannel.
  • muted/hidden channel and deliveryEventsEnabled = false.

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 29d2fdb and a8f4c8b.

📒 Files selected for processing (3)
  • Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift (2 hunks)
  • Sources/StreamChat/Models/ChannelRead.swift (2 hunks)
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChannelRead.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChannelRead.swift
🧠 Learnings (2)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChannelRead.swift
🧬 Code graph analysis (1)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/MessageDeliveryCriteriaValidator_Mock.swift (1)
  • canMarkMessageAsDelivered (16-27)
TestTools/StreamChatTestTools/Mocks/StreamChat/Models/MessageDeliveryCriteriaValidator_Mock.swift (1)
  • canMarkMessageAsDelivered (16-27)
Sources/StreamChat/Models/Channel.swift (1)
  • read (390-392)
🔇 Additional comments (2)
Sources/StreamChat/Models/ChannelRead.swift (1)

27-41: LGTM!

The initializer correctly incorporates the new delivery parameters and assigns them to the corresponding properties. The parameter ordering maintains backward compatibility.

Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)

9-27: Confirm intended access level for protocol/struct.

Both MessageDeliveryCriteriaValidating and MessageDeliveryCriteriaValidator are internal; is that intentional? If these are part of the public API (per PR scope), mark the types public to align with their usage; otherwise keep internal. If making public, update docs per guidelines. As per coding guidelines.

Option if public:

-protocol MessageDeliveryCriteriaValidating {
+public protocol MessageDeliveryCriteriaValidating {
@@
-struct MessageDeliveryCriteriaValidator: MessageDeliveryCriteriaValidating {
+public struct MessageDeliveryCriteriaValidator: MessageDeliveryCriteriaValidating {
-    init() {}
+    public init() {}
@@
-    func canMarkMessageAsDelivered(
+    public func canMarkMessageAsDelivered(

@nuno-vieira nuno-vieira force-pushed the add/message-delivered-status branch from a8f4c8b to 60d24a8 Compare October 30, 2025 18:22
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: 0

♻️ Duplicate comments (1)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)

30-40: Documentation inconsistency with read state handling.

The documentation states "The current user has a read state in the channel" as a requirement (line 37), but the implementation at line 81 returns true when the read state is missing. This contradicts the documented criteria. Either update the documentation to reflect that a missing read state is treated as allowed, or enforce the requirement in the implementation.

🧹 Nitpick comments (1)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)

46-82: Validation logic is comprehensive and sound.

The implementation correctly validates all the key criteria for message delivery eligibility. The timestamp comparisons and privacy checks are well-structured.

Optional: Line 72 could use contains(where:) instead of map(_:).contains(_:) to avoid creating an intermediate array:

-if currentUser.mutedUsers.map(\.id).contains(message.author.id) {
+if currentUser.mutedUsers.contains(where: { $0.id == message.author.id }) {
    return false
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 60d24a8 and e743f7e.

📒 Files selected for processing (2)
  • Sources/StreamChat/Models/Channel.swift (1 hunks)
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/StreamChat/Models/Channel.swift
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
🧠 Learnings (2)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
🧬 Code graph analysis (1)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/MessageDeliveryCriteriaValidator_Mock.swift (1)
  • canMarkMessageAsDelivered (16-27)
TestTools/StreamChatTestTools/Mocks/StreamChat/Models/MessageDeliveryCriteriaValidator_Mock.swift (1)
  • canMarkMessageAsDelivered (16-27)
Sources/StreamChat/Models/Channel.swift (1)
  • read (390-392)
🔇 Additional comments (2)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (2)

9-22: Protocol definition is clean and well-documented.

The protocol provides a clear contract for delivery validation. Access control is now consistent with the struct implementation.


85-89: Clean encapsulation of channel-level delivery criteria.

The private extension appropriately groups the channel state checks, making the validator logic more readable.

Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

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

Looks good to me ✅ Left few small comments.
"Should we provide the double grey checkmark as part of the UI SDK if deliveries are enabled?"
Yes, we already have it, right? It just needs to consider the delivery events from now on.

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: 3

♻️ Duplicate comments (1)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)

65-71: Clarify documentation for missing read state behavior.

The implementation returns true when channel.read(for: currentUser.id) is nil (line 70), treating a missing read state as allowing delivery. However, the documentation doesn't explicitly state this behavior. Per past review comments, this should be clarified.

Consider updating the function documentation to clarify the missing read state behavior:

     /// - Parameters:
     ///   - message: The message to check for delivery status.
     ///   - currentUser: The current user who would mark the message as delivered.
     ///   - channel: The channel containing the message.
+    /// - Note: If the current user has no read state in the channel, the message is allowed to be marked as delivered.
     /// - Returns: `true` if the message can be marked as delivered, `false` otherwise.

Alternatively, verify via tests that this behavior is intentional:

#!/bin/bash
# Search for tests covering the missing read state scenario
ast-grep --pattern 'func test_$$$_whenUserHasNoReadState$$$() {
  $$$
}'
rg -n "read.*nil|no.*read.*state" Tests/StreamChatTests/Models/MessageDeliveryCriteriaValidator_Tests.swift
🧹 Nitpick comments (4)
CHANGELOG.md (1)

7-8: Consider expanding the changelog entry to follow established patterns.

The current entry is minimal compared to similar multi-API additions elsewhere in the CHANGELOG (e.g., lines 41–45 for push preferences, lines 75–78 for location sharing, lines 204–208 for message reminders). Given the PR introduces multiple new public APIs (ChatRemoteNotificationHandler.markMessageAsDelivered(), ChatChannel.reads(), ChatChannel.deliveredReads(), ChatChannelRead.lastDeliveredAt, ChatChannelRead.lastDeliveredMessageId), consider adding sub-bullets to document the specific new capabilities:

  ### ✅ Added
- - Add support for message delivered info [#3846](https://github.com/GetStream/stream-chat-swift/pull/3846)
+ - Add support for message delivered info [#3846](https://github.com/GetStream/stream-chat-swift/pull/3846)
+   - Add `ChatRemoteNotificationHandler.markMessageAsDelivered(deliveries:)`
+   - Add `ChatChannel.reads(message:)` and `ChatChannel.deliveredReads(message:)`
+   - Add `ChatChannelRead.lastDeliveredAt` and `ChatChannelRead.lastDeliveredMessageId`

Additionally, the PR objectives mention UI changes (double grey checkmark for delivered-but-not-read messages). Consider whether a corresponding entry under ## StreamChatUI is warranted. ,

Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift (2)

225-269: Clarify or simplify the lastDeliveredAt setup in this test.

This test sets lastDeliveredAt to 10 seconds before the message was created (line 254: messageCreatedAt.addingTimeInterval(-10)), which differs from the enabled test where it's set after.

If the intent is to verify that delivery status is not displayed when deliveryEventsEnabled: false, consider using the same delivery data as the first test (delivered state) to clearly demonstrate that the feature flag controls visibility. The existing read tests (lines 151-177) follow this pattern—they use identical message data and only toggle readEventsEnabled.

Alternatively, if this setup is intentional to test handling of stale/inconsistent data, add a comment explaining the rationale.

Apply this diff to align with the enabled test and the pattern used in read tests:

                 reads: [
                     .mock(
                         lastReadAt: Date.distantPast,
                         lastReadMessageId: nil,
                         unreadMessagesCount: 0,
                         user: otherUser,
-                        lastDeliveredAt: messageCreatedAt.addingTimeInterval(-10),
+                        lastDeliveredAt: messageCreatedAt.addingTimeInterval(10),
                         lastDeliveredMessageId: deliveredMessage.id
                     )
                 ],

179-269: Consider adding test coverage for "sent but not delivered" state.

The new tests cover delivered messages with delivery events enabled/disabled. To complete the delivery state spectrum (analogous to the existing read tests), consider adding a test for a message that is sent but has not yet been delivered, with deliveryEventsEnabled: true. This would verify the UI correctly displays the "sent" status when delivery tracking is active but no delivery receipt has been recorded yet.

Example setup:

  • Message from current user at time 100
  • deliveryEventsEnabled: true
  • lastDeliveredAt set to a time before the message (e.g., 90) or no matching lastDeliveredMessageId
Sources/StreamChat/Models/ChatMessage.swift (1)

535-535: Consider refining the deprecation message.

The deprecation message refers to "The function" but could be more precise: "The method deliveryStatus(for:) is preferred" since this is an instance method, not a function.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 78f2b9f and 0449171.

⛔ Files ignored due to path filters (10)
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsDisabled.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsDisabled.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsEnabled.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsEnabled.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusCheckmarkView_Tests/test_appearance_delivered.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusCheckmarkView_Tests/test_appearance_delivered.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusView_Tests/test_appearance_whenMessageIsDeliveredInDirectMessagesChannel.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusView_Tests/test_appearance_whenMessageIsDeliveredInDirectMessagesChannel.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusView_Tests/test_appearance_whenMessageIsDeliveredInGroupChannel.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/__Snapshots__/ChatMessageDeliveryStatusView_Tests/test_appearance_whenMessageIsDeliveredInGroupChannel.default-light.png is excluded by !**/*.png
📒 Files selected for processing (17)
  • CHANGELOG.md (1 hunks)
  • DemoApp/Shared/DemoUsers.swift (1 hunks)
  • Sources/StreamChat/ChatClient.swift (3 hunks)
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (4 hunks)
  • Sources/StreamChat/Models/ChatMessage.swift (3 hunks)
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1 hunks)
  • Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift (2 hunks)
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView.swift (1 hunks)
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift (2 hunks)
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift (1 hunks)
  • StreamChat.xcodeproj/project.pbxproj (44 hunks)
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (18 hunks)
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift (5 hunks)
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift (1 hunks)
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift (1 hunks)
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift (1 hunks)
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • DemoApp/Shared/DemoUsers.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChatMessage.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift
  • Sources/StreamChat/ChatClient.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Tests/{StreamChatTests,StreamChatUITests}/**/*.swift: Add or extend tests in the matching module’s Tests folder
Prefer using provided test fakes/mocks in tests when possible

Files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
Tests/StreamChatUITests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover view controllers and UI behaviors of StreamChatUI

Files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift
  • Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChatMessage.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift
  • Sources/StreamChat/ChatClient.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
Tests/StreamChatTests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover core models and API surface of StreamChat

Files:

  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Update CHANGELOG for user-visible SDK changes

Files:

  • CHANGELOG.md
🧠 Learnings (6)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • DemoApp/Shared/DemoUsers.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • StreamChat.xcodeproj/project.pbxproj
  • CHANGELOG.md
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Add or extend tests in the matching module’s Tests folder

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Prefer using provided test fakes/mocks in tests when possible

Applied to files:

  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to CHANGELOG.md : Update CHANGELOG for user-visible SDK changes

Applied to files:

  • DemoApp/Shared/DemoUsers.swift
  • CHANGELOG.md
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
  • Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift
  • Sources/StreamChat/Models/ChatMessage.swift
  • Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift
  • StreamChat.xcodeproj/project.pbxproj
  • CHANGELOG.md
  • Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift
🧬 Code graph analysis (12)
Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
  • mock (11-104)
  • mock (108-126)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatUser_Mock.swift (1)
  • mock (10-46)
Sources/StreamChat/Models/Channel.swift (1)
  • reads (403-407)
Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift (1)
Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift (1)
  • updateContent (319-369)
Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift (4)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
  • mock (11-104)
  • mock (108-126)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatUser_Mock.swift (1)
  • mock (10-46)
Sources/StreamChat/Models/Channel.swift (1)
  • reads (403-407)
Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift (1)
  • optionsForMessage (33-117)
Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift (1)
Sources/StreamChat/Models/ChatMessage.swift (1)
  • deliveryStatus (558-581)
Tests/StreamChatTests/Models/ChatMessage_Tests.swift (4)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (3)
  • mock (10-52)
  • mock (57-73)
  • mock (78-151)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
  • mock (11-104)
  • mock (108-126)
Sources/StreamChat/Models/ChatMessage.swift (1)
  • deliveryStatus (558-581)
Sources/StreamChat/Models/Channel.swift (2)
  • reads (403-407)
  • read (390-392)
Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift (2)
Sources/StreamChat/Models/ChatMessage.swift (1)
  • deliveryStatus (558-581)
Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift (1)
  • channel (1815-1833)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)
Sources/StreamChat/Models/Channel.swift (1)
  • read (390-392)
Sources/StreamChat/Models/ChatMessage.swift (1)
Sources/StreamChat/Models/Channel.swift (2)
  • deliveredReads (418-425)
  • read (390-392)
Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift (1)
Sources/StreamChat/Models/ChatMessage.swift (1)
  • deliveryStatus (558-581)
Sources/StreamChat/ChatClient.swift (1)
Sources/StreamChat/ChatClientFactory.swift (1)
  • makeEventNotificationCenter (114-146)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (5)
Sources/StreamChat/Utils/Logger/Logger.swift (2)
  • error (395-410)
  • log (277-314)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (6)
  • currentUserController (13-15)
  • currentUserController (745-748)
  • currentUserController (750-753)
  • currentUserController (755-758)
  • currentUserController (768-772)
  • currentUserController (774-777)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)
  • canMarkMessageAsDelivered (35-71)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (1)
  • markMessagesAsDelivered (73-103)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (2)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • cleanUp (131-165)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (2)
  • synchronize (157-162)
  • loadNextChannels (173-195)
🔇 Additional comments (21)
DemoApp/Shared/DemoUsers.swift (1)

8-8: Verify if this API key change is intentional.

The default API key has been changed from DemoApiKeys.frankfurtC1 to DemoApiKeys.usEastC6. This change is not mentioned in the PR description or objectives, which focus on message delivery status functionality.

Please confirm:

  • Is this change intentional or an accidental testing artifact?
  • If intentional, should it be documented in the PR description and does it need to be part of this delivery status PR?
  • Could this affect existing demo app users, tests, or documentation?
Tests/StreamChatUITests/SnapshotTests/ChatChannelList/ChatChannelListItemView_Tests.swift (1)

179-223: LGTM! Clear test for delivered message with delivery events enabled.

The test correctly sets up a delivered-but-not-read message scenario with lastDeliveredAt after the message creation time and deliveryEventsEnabled: true, ensuring the snapshot captures the delivery UI state.

Sources/StreamChat/ChatClient.swift (3)

81-82: LGTM!

Internal property declaration is clean and appropriately documented for infrastructure wiring.


167-180: LGTM!

Dependency construction follows the correct order and the environment builder pattern used throughout the codebase. The channelDeliveryTracker correctly receives currentUserUpdater as a dependency.


239-239: LGTM!

Assignment is consistent with the initialization pattern for other repository properties.

Sources/StreamChat/Models/ChatMessage.swift (1)

731-737: LGTM!

The enum documentation clearly distinguishes between sent (server received), delivered (device received), and read states. The semantic progression is well-defined.

Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView_Tests.swift (1)

63-78: LGTM!

Test coverage for the delivered status follows the established pattern and covers both appearance and accessibility concerns.

Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver.swift (1)

209-217: LGTM!

Migration to the channel-aware API is correct, and the delivered status visibility is appropriately gated by the deliveryEventsEnabled configuration flag, following the same pattern as read events.

Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusCheckmarkView.swift (1)

45-45: LGTM!

Grouping .delivered with .read for the same checkmark image is correct. The tint color differentiation (lines 56-62) ensures delivered shows as grey while read shows as accent color, matching the intended UX.

Sources/StreamChatUI/ChatChannelList/ChatChannelListItemView.swift (1)

216-229: LGTM!

The migration to the channel-aware delivery status API is correct, and the delivered case is appropriately gated by deliveryEventsEnabled, consistent with how other delivery statuses are handled.

Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView_Tests.swift (1)

117-197: LGTM!

Comprehensive test coverage for the delivered status in both direct and group channels. The tests properly configure channel delivery events and channel reads to exercise the delivered state rendering.

Sources/StreamChatUI/ChatMessageList/ChatMessage/ChatMessageDeliveryStatusView.swift (1)

84-84: LGTM!

Correctly uses the new facade property, simplifying access to delivery status.

Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessage/ChatMessageLayoutOptionsResolver_Tests.swift (1)

1758-1846: LGTM! Well-structured tests for delivery events feature flag.

The two new test methods correctly verify that the delivery status indicator respects the deliveryEventsEnabled configuration:

  • The first test confirms that when delivery events are enabled and a message is delivered (but not read), the indicator is included in layout options.
  • The second test ensures the indicator is properly gated when delivery events are disabled, even with the same delivered message state.

Both tests follow existing patterns in the file, use appropriate mocks, and complement the existing delivery status test coverage.

StreamChat.xcodeproj/project.pbxproj (1)

1467-1476: LGTM! Project structure is well-organized.

All new delivery-related files are properly registered in the Xcode project with correct build phases, target memberships, and group organization. The structure follows project conventions with appropriate separation of sources, tests, mocks, and resources.

Also applies to: 1668-1672, 1734-1735, 1766-1766, 1796-1798, 1818-1819, 4361-4362, 4367-4368, 4497-4499, 4547-4548, 4572-4572, 4595-4596, 4611-4611, 5953-5953, 5976-5976, 6089-6089, 6138-6138, 6759-6759, 7003-7003, 7219-7219, 7229-7229, 7272-7272, 7335-7335, 7507-7507, 7529-7529, 8766-8773, 9130-9138, 10585-10585, 11386-11386, 11459-11459, 11604-11604, 11811-11811, 11843-11843, 11856-11856, 12114-12114, 12124-12124, 12175-12175, 12246-12246, 12364-12364, 12417-12417, 12806-12806, 12863-12863, 12940-12940, 13065-13065, 13094-13094

Tests/StreamChatTests/Models/ChatMessage_Tests.swift (1)

289-359: LGTM! Comprehensive test coverage for delivered status.

The tests correctly verify that messages return .delivered status when they have been delivered but not read. The test setup properly creates channel reads with lastDeliveredAt and lastDeliveredMessageId to establish the delivered state, and both regular messages and thread replies are covered.

Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (3)

60-69: LGTM! Dependency injection follows established patterns.

The lazy properties for currentUserUpdater and deliveryCriteriaValidator are correctly initialized via environment builders, enabling dependency injection for testing while maintaining the same pattern used by the existing worker property.


238-263: LGTM! Defensive implementation with appropriate error handling.

The method correctly filters channels based on delivery criteria, avoids unnecessary API calls when there are no deliveries, and logs errors for debugging. The early return when currentUser is nil and the empty deliveries check are good defensive practices.


187-189: LGTM! Integration points correctly trigger automatic delivery marking.

The calls to markChannelsAsDeliveredIfNeeded are appropriately placed after successful channel fetches in both loadNextChannels and updateChannelList, enabling automatic delivery marking as intended by the feature design.

Also applies to: 226-229

Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (2)

37-37: LGTM! Test infrastructure correctly wired.

The test environment properly integrates the new currentUserUpdater and deliveryCriteriaValidator mocks, following the established patterns for dependency injection. The cleanup in tearDown ensures mocks are properly reset between tests.

Also applies to: 2110-2111, 2134-2145


868-956: Tests look good—they thoroughly cover the delivery-marking functionality.

The tests verify that markChannelsAsDelivered is called with correct messages after successful channel updates and that only messages meeting delivery criteria are marked. Both synchronize and loadNextChannels paths are well covered.

The implementation already handles edge cases gracefully: nil currentUser is guarded against with an early return, and errors from markMessagesAsDelivered are logged but don't break the flow. The tests focus appropriately on the success path and filtering logic, which is the core business behavior.

Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)

9-26: LGTM! Access control is appropriate for internal usage.

The protocol and struct correctly use internal (default) access since they are only consumed by internal components like ChannelListController.Environment. The access control issue mentioned in past reviews has been resolved.

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: 0

🧹 Nitpick comments (1)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (1)

237-263: Consider weak self capture in completion handler.

The implementation correctly filters channels and builds delivery info, but the completion handler at line 258 doesn't use [weak self]. If the controller is deallocated before the API call completes, this prevents the error from being logged. While this is acceptable, using weak self would be more defensive.

Consider this adjustment:

-        currentUserUpdater.markMessagesAsDelivered(deliveries) { error in
+        currentUserUpdater.markMessagesAsDelivered(deliveries) { [weak self] error in
             if let error = error {
-                log.error("Failed to mark channels as delivered: \(error)")
+                self?.log.error("Failed to mark channels as delivered: \(error)")
             }
         }

Note: If log is a global function rather than an instance method, the weak self capture would only apply if additional instance state is accessed in future enhancements.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2474ea0 and 496ec1d.

📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (5 hunks)
  • Sources/StreamChat/Models/ChatMessage.swift (3 hunks)
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (19 hunks)
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (4)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChat/Models/ChatMessage.swift
  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/Models/ChatMessage.swift
  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Tests/{StreamChatTests,StreamChatUITests}/**/*.swift: Add or extend tests in the matching module’s Tests folder
Prefer using provided test fakes/mocks in tests when possible

Files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
Tests/StreamChatTests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover core models and API surface of StreamChat

Files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
🧠 Learnings (6)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • Sources/StreamChat/Models/ChatMessage.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Prefer using provided test fakes/mocks in tests when possible

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Add or extend tests in the matching module’s Tests folder

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift
  • Tests/StreamChatTests/Models/ChatMessage_Tests.swift
📚 Learning: 2025-11-03T10:25:07.731Z
Learnt from: laevandus
Repo: GetStream/stream-chat-swift PR: 3863
File: Sources/StreamChat/Repositories/SyncOperations.swift:124-126
Timestamp: 2025-11-03T10:25:07.731Z
Learning: In Sources/StreamChat/Repositories/SyncOperations.swift, the SyncEventsOperation deliberately limits syncing to a maximum of 100 channels (Array(channelIds.prefix(100))) to avoid server-side errors. Channels beyond the first 100 are intentionally not synced as part of this design decision.

Applied to files:

  • Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift
🧬 Code graph analysis (4)
Sources/StreamChat/Models/ChatMessage.swift (1)
Sources/StreamChat/Models/Channel.swift (2)
  • deliveredReads (418-425)
  • read (390-392)
Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (3)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (1)
  • loadNextChannels (173-195)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • cleanUp (131-165)
TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift (1)
  • writeSynchronously (175-182)
Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (6)
Sources/StreamChat/Utils/Logger/Logger.swift (2)
  • error (395-410)
  • log (277-314)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (6)
  • currentUserController (13-15)
  • currentUserController (745-748)
  • currentUserController (750-753)
  • currentUserController (755-758)
  • currentUserController (768-772)
  • currentUserController (774-777)
Sources/StreamChat/Models/MessageDelivery/MessageDeliveryCriteriaValidator.swift (1)
  • canMarkMessageAsDelivered (35-71)
Sources/StreamChat/WebSocketClient/ChannelDeliveryTracker.swift (1)
  • markMessagesAsDelivered (73-103)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • markMessagesAsDelivered (117-128)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • markMessagesAsDelivered (283-294)
Tests/StreamChatTests/Models/ChatMessage_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (3)
  • mock (10-52)
  • mock (57-73)
  • mock (78-151)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
  • mock (11-104)
  • mock (108-126)
Sources/StreamChat/Models/ChatMessage.swift (1)
  • deliveryStatus (563-586)
🔇 Additional comments (15)
Sources/StreamChat/Models/ChatMessage.swift (3)

535-555: Clear deprecation strategy.

The deprecation annotation properly guides users to the new channel-aware API while preserving backward compatibility.


557-586: Well-documented channel-aware delivery status implementation.

The documentation comprehensively addresses the past review comment by including behavior description, parameter details, and return value conditions. The implementation correctly integrates channel.deliveredReads(for:) to determine delivered status and maintains proper precedence: read > delivered > sent.

Based on past review comments.


736-742: Clear documentation for delivery status states.

The updated documentation properly distinguishes between .sent (on server but not yet delivered) and the new .delivered (reached user's device) states.

Tests/StreamChatTests/Models/ChatMessage_Tests.swift (4)

127-183: Comprehensive coverage of nil cases.

The tests properly verify that deliveryStatus(for:) returns nil for messages authored by others and for non-regular/non-reply message types. The consistent pattern of creating a channel and matching message CID to channel CID is correct.


185-287: Thorough coverage of local states and sent status.

The tests properly verify pending, failed, and sent statuses for both regular messages and thread replies, with correct channel context and CID matching throughout.


289-359: Excellent coverage of delivered status scenarios.

The tests properly verify the delivered status for both regular messages and thread replies. The thread reply test at Line 352 correctly accounts for the 1-second error interval used in the deliveredReads(for:) implementation.


361-428: Comprehensive read status tests with correct precedence validation.

The tests properly verify read status and that read status takes precedence over delivered status. The CID consistency issue flagged in the past review comment has been resolved—Line 402 now correctly uses cid: cid to match the channel.

Based on past review comments.

Tests/StreamChatTests/Controllers/ChannelListController/ChannelListController_Tests.swift (4)

23-28: LGTM! Proper test setup for mock injection.

Moving controller initialization to setUp() enables the test environment to inject mocks via the environment parameter, which is essential for testing the delivery marking behavior.


869-958: LGTM! Comprehensive test coverage for delivery marking.

The three new tests properly cover:

  • Delivery marking after synchronize() success
  • Delivery marking after loadNextChannels() success
  • Selective marking based on delivery criteria validation

The tests correctly verify the CurrentUserUpdater interactions and validate the delivered message details.


2113-2148: LGTM! Proper environment wiring for new dependencies.

The TestEnvironment correctly extends the environment with builders for currentUserUpdater and deliveryCriteriaValidator, enabling mock injection for testing delivery marking behavior. The pattern matches the existing channelListUpdater setup.


1774-1850: Test data updated for new delivery fields.

The channel read payloads are properly extended with lastDeliveredAt and lastDeliveredMessageId fields to match the updated ChannelRead model. Using nil values is appropriate for these tests that don't focus on delivery functionality.

Sources/StreamChat/Controllers/ChannelListController/ChannelListController.swift (4)

59-68: LGTM! Proper dependency injection for delivery tracking.

The new currentUserUpdater and deliveryCriteriaValidator properties are correctly initialized via environment builders, enabling testability through mock injection.


188-188: LGTM! Delivery marking properly integrated after channel updates.

The calls to markChannelsAsDeliveredIfNeeded are correctly placed after successful channel list updates in both loadNextChannels and updateChannelList (synchronize path). The fire-and-forget approach with error logging is appropriate since delivery marking failures shouldn't block the main channel list flow.

Also applies to: 226-228


299-306: LGTM! Environment builders properly structured.

The new builders follow the existing pattern and provide sensible defaults for production while enabling mock injection in tests.


153-153: LGTM! Validator properly initialized.

The deliveryCriteriaValidator is correctly initialized from the environment builder during controller construction.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 523aa0f and cc2cf64.

⛔ Files ignored due to path filters (6)
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsDisabled.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsDisabled.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsEnabled.default-dark.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_appearance_deliveredPreviewMessageFromCurrentUser_deliveryEventsEnabled.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampOlderDate.default-light.png is excluded by !**/*.png
  • Tests/StreamChatUITests/SnapshotTests/Gallery/__Snapshots__/GalleryVC_Tests/test_snapshotWithMessageTimestampToday.default-light.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

**/*.swift: Respect .swiftlint.yml rules; do not suppress SwiftLint rules broadly—scope and justify any exceptions
Adhere to the project’s zero warnings policy—fix new warnings and avoid introducing any

Files:

  • Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
🧠 Learnings (2)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Sources/{StreamChat,StreamChatUI}/**/*.swift : When altering public API, update inline documentation comments in source

Applied to files:

  • Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
🧬 Code graph analysis (1)
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (3)
Sources/StreamChat/WebSocketClient/Events/EventType.swift (1)
  • event (186-267)
Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (1)
  • event (208-210)
Tests/StreamChatUITests/Mocks/ChatMessageList/ChatMessageListView_Mock.swift (1)
  • reloadRows (22-25)
⏰ 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). (3)
  • GitHub Check: Build LLC + UI (Xcode 15)
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Metrics

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamChat 8.28 MB 7.88 MB -409 KB 🚀
StreamChatUI 4.89 MB 4.89 MB 0 KB 🟢

@Stream-SDK-Bot
Copy link
Collaborator

StreamChat XCSize

Object Diff (bytes)
MessageEvents.o -461607
ChatRemoteNotificationHandler.o +13546
MemberController.o -11376
ChannelDeliveredPayload.o +7086
ChannelDeliveryTracker.o +6146
Show 45 more objects
Object Diff (bytes)
MessageEditor.o -5988
MessageDeliveryCriteriaValidator.o +2640
ChannelListController.o +2617
ChannelDeliveredMiddleware.o +2505
WebSocketConnectPayload.o +2355
Channel.o +2112
EndpointPath.o +1965
ChannelRead.o +1834
ChannelListPayload.o +1771
ChatMessage.o +1560
Logger.o +1322
ChatClient+Environment.o +1287
EventPayload.o +1275
CurrentUserController.o -1235
DeliveredMessageInfo.o +1166
ChannelController.o +1144
ErrorPayload.o +836
ThreadQuery.o +716
ChannelReadDTO.o +714
CurrentUserUpdater.o +612
LivestreamChannelController.o -536
ReminderPayloads.o +464
Chat.o +436
MemberPayload.o -412
UserInfo.o +381
Deprecations.o +316
MessageSender.o -258
ChatMessageVideoAttachment.o +256
ManualEventHandler.o -252
ConnectionRepository.o +228
AnyAttachmentPayload.o +220
CurrentUserDTO.o +218
ChatClient.o +207
EventType.o +198
ChatClientFactory.o +172
ChannelConfigDTO.o +162
EventsController.o +160
ChannelPayload+asModel.o +136
MessageController.o +104
ChannelListLinker.o +92
UserPayloads.o +92
ChannelDTO.o +88
ChannelList.o +88
CurrentUser.o +68
PollVoteListController.o +48

@Stream-SDK-Bot
Copy link
Collaborator

StreamChatUI XCSize

Object Diff (bytes)
ChatChannelVC.o +332
ChatChannelListItemView.o +136
ChatMessageLayoutOptionsResolver.o +92
ChatThreadVC.o -84
ChatMessageDeliveryStatusCheckmarkView.o +52

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 3, 2025


guard !deliveredMessages.isEmpty else { return }

self?.currentUserUpdater.markMessagesAsDelivered(deliveredMessages) { [weak self] error in
Copy link
Member

Choose a reason for hiding this comment

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

The current API only supports upto 100 messages in payload. So you might also want to do it in a batch of 100 messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✅ Feature An issue or PR related to a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants