-
Notifications
You must be signed in to change notification settings - Fork 229
Mark messages as unread using timestamp #3885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds timestamp-based "mark unread" support: new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant ChatChannelController
participant ReadStateHandler
participant MessageRepository
participant ChannelUpdater
participant ChannelRepository
participant APIClient
participant Database
Client->>ChatChannelController: markUnread(from: Date)
ChatChannelController->>ReadStateHandler: markUnread(from: .messageTimestamp(date), in: channel)
ReadStateHandler->>MessageRepository: getMessage(before: .messageTimestamp(date))
MessageRepository->>Database: loadMessage(beforeOrEqual: timestamp)
Database-->>MessageRepository: MessageDTO? (found / nil)
MessageRepository-->>ReadStateHandler: Result<MessageId?, Error>
ReadStateHandler->>ChannelUpdater: markUnread(from: .messageTimestamp(date))
ChannelUpdater->>ChannelRepository: markUnread(for: cid, from: .messageTimestamp(date))
ChannelRepository->>APIClient: POST /channels/{type}/{id}/unread (body: MarkUnreadPayload)
APIClient-->>ChannelRepository: EmptyResponse
ChannelRepository->>Database: markChannelAsUnread(for: cid, from: .messageTimestamp)
Database-->>ChannelRepository: updated ChatChannel
ChannelRepository-->>ChannelUpdater: Result<ChatChannel, Error>
ChannelUpdater-->>ReadStateHandler: Result<ChatChannel, Error>
ReadStateHandler-->>ChatChannelController: Result<ChatChannel, Error>
ChatChannelController-->>Client: completion(Result<ChatChannel, Error>)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
1303-1377: markUnread controller APIs are correct; tighten timestamp doc wording
- The message-id overload now correctly wraps
messageIdin.messageId(...)before delegating toreadStateHandler, reusing the existing guards and callback pattern.- The new
markUnread(from timestamp: Date, ...)overload reuses the same guards and delegates via.messageTimestamp(timestamp), which aligns withReadStateHandler/ChannelReadDTO’s use ofMarkUnreadCriteria.The doc comment for the timestamp overload says it:
finds the first message with a creation timestamp greater than the provided timestamp
but the DB layer actually:
- resolves a message at or before the timestamp (via
loadMessage(beforeOrEqual:timestamp, ...)), and- treats messages after that resolved message as unread, doing nothing if no such message can be found.
To avoid confusion in edge cases (e.g. timestamp earlier than the oldest message), consider rephrasing along the lines of:
This method resolves the last message with a creation timestamp less than or equal to the provided timestamp, and marks all later messages as unread. If no such message can be found, the operation completes without error and leaves the unread state unchanged.
Also, since this is a new public API on
ChatChannelController, ensure there is a corresponding CHANGELOG entry and migration note.Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4135-4328: Mark-unread tests align with new criteria-based flow; fix misleading comment and consider timestamp coverageThe updated mark‑unread tests correctly drive
controller.markUnread(from: MessageId)and assert thatChannelUpdater_MockreceivesmarkUnread_criteria == .messageId(messageId), which matches the newMarkUnreadCriteria‑based pipeline. The various error/no‑error branches (missing channel, read events disabled, marking read in progress, missing current user) remain consistent withReadStateHandler.markUnread.One small issue: in
test_markUnread_whenIsNotMarkingAsRead_andCurrentUserIdIsPresent_whenThereAreNoMessages_whenUpdaterSucceeds, the comment saying “we fallback to the passed messageId as lastReadMessageId” contradicts the assertionXCTAssertNil(updater.markUnread_lastReadMessageId). Please update or remove the comment to reflect actual behaviour.If there isn’t already a separate test exercising
ChatChannelController.markUnread(from timestamp: Date)(possibly in another suite), it would be good to add one for parity with the message‑ID path.
🧹 Nitpick comments (7)
Sources/StreamChat/StateLayer/Chat.swift (1)
937-950: Async Chat.markUnread overloads are wired correctly; add release notesBoth
markUnread(from messageId:)and the newmarkUnread(from timestamp:)correctly delegate viaMarkUnreadCriteriatoreadStateHandlerand reuse the existingChannelNotCreatedYeterror path. This keeps the async state-layer API consistent with the controller layer.Given this is a new public API surface in
StreamChat, make sure the CHANGELOG and migration docs call out:
- the new
Chat.markUnread(from: Date)API, and- the semantic equivalence between the message-id and timestamp variants.
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (1)
425-630: Timestamp-based partial unread tests cover main paths; consider one more edge caseThe new
messageTimestamptests for:
- messages present (expecting correct
lastReadAt,unreadMessageCount, andlastReadMessageId), and- explicit
lastReadAt/unreadMessagesCountoverrides,mirror the existing message-id tests and validate that timestamp criteria behave identically at the DTO level.
If you want to harden behavior further, you could add a test where the timestamp is before the earliest message in the channel, asserting that the unread state stays unchanged (matching the current
guard let message = findMessageDTO()early return).Sources/StreamChat/Repositories/MessageRepository.swift (1)
341-371: MarkUnreadCriteria handling in repository looks correct; duplication is acceptable but could be reducedThe new
getMessage(before unreadCriteria: MarkUnreadCriteria, in:cid, ...)correctly routes.messageIdto the existingloadMessage(before: ...)path and.messageTimestamptoloadMessage(beforeOrEqual: ...), reusing shared config and preserving error handling. If you touch this again, consider extracting a tiny helper to avoid repeating the fetch+.idpattern in both switch cases, but it’s fine as-is.Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
610-625: Timestamp-basedloadMessagelooks correct; consider aligning with other fetch helpersThe predicate and sort (latest channel message with
createdAt <= timestamp) match the intended semantics and mirror the existingloadMessage(before:id:cid:...)path. For consistency with other fetch helpers, you might consider also callingMessageDTO.applyPrefetchingState(to:)and/or reusing theload(request,context:)helper, but this is optional given the single-object fetch.Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (1)
141-175: Unread-by-timestamp resolution looks correct; consider unifying client-config handlingThe refactor cleanly supports both
.messageIdand.messageTimestamp, and the timestamp branch correctly reusesMessageDTO.loadMessage(beforeOrEqual:cid:deletedMessagesVisibility:shouldShowShadowedMessages:context:)so unread is anchored to the latest visible message at/before the timestamp.One small consistency tweak to consider: for the timestamp branch you currently fall back to
.alwaysVisibleandfalsewhenchatClientConfigisnil, whereas the existingloadMessage(before:id:cid:)helper guards on a non-nil config and returnsnil. Aligning these behaviors (e.g., early-return when config is missing) would make the criteria paths behave more uniformly.Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift (1)
112-137: RepositorymarkUnreadtests match new payload shape; consider adding a timestamp-criteria caseThe updated tests correctly assert that
ChannelRepository.markUnreadpasses.messageId(messageId)viaMarkUnreadPayload(criteria:userId:)and hits the expected.markUnread(cid:payload:)endpoint.Since the feature now also supports
.messageTimestamp, it would be useful to add a companion test that callsmarkUnread(from: .messageTimestamp(...))and asserts the encoded payload’scriteriais.messageTimestamp(...), to lock in both branches of the new enum.Also applies to: 139-160
Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift (1)
1727-1739: UpdatermarkUnreadtests validate id-based criteria; add a timestamp variant for full coverageThese tests correctly ensure that
ChannelUpdater.markUnreadforwards.messageId(messageId)andlastReadMessageIdtoChannelRepository_Mockand that success/error results are propagated.To fully exercise the new
MarkUnreadCriteriasurface, consider an additional test that callsmarkUnread(from: .messageTimestamp(timestamp))and assertschannelRepository.markUnreadCriteriais.messageTimestamp(timestamp)and the completion semantics remain identical. This would complement the existing id-based checks.Also applies to: 1741-1768
📜 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.
📒 Files selected for processing (25)
CHANGELOG.md(1 hunks)DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift(1 hunks)Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swift(1 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift(1 hunks)Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift(1 hunks)Sources/StreamChat/Database/DatabaseSession.swift(1 hunks)Sources/StreamChat/Repositories/ChannelRepository.swift(2 hunks)Sources/StreamChat/Repositories/MessageRepository.swift(2 hunks)Sources/StreamChat/StateLayer/Chat.swift(1 hunks)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift(1 hunks)Sources/StreamChat/Workers/ChannelUpdater.swift(1 hunks)Sources/StreamChat/Workers/ReadStateHandler.swift(3 hunks)Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift(1 hunks)StreamChat.xcodeproj/project.pbxproj(5 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift(1 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift(2 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift(3 hunks)Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift(2 hunks)Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift(7 hunks)Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift(6 hunks)Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift(3 hunks)Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift(1 hunks)Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift(3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftSources/StreamChat/Database/DTOs/MessageDTO.swiftSources/StreamChat/Workers/ReadStateHandler.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
Tests/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swift
Sources/StreamChat/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChat/Workers/ChannelUpdater.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swiftSources/StreamChat/Database/DatabaseSession.swiftSources/StreamChat/Repositories/MessageRepository.swiftSources/StreamChat/Database/DTOs/MessageDTO.swiftSources/StreamChat/Workers/ReadStateHandler.swiftSources/StreamChat/StateLayer/Chat.swift
CHANGELOG*
📄 CodeRabbit inference engine (AGENTS.md)
Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Files:
CHANGELOG.md
Sources/StreamChatUI/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Applied to files:
Sources/StreamChat/Workers/ChannelUpdater.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Prioritize backwards compatibility and API stability when changing code in the Stream iOS Chat SDK
Applied to files:
CHANGELOG.mdTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
🧬 Code graph analysis (20)
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (5)
markChannelAsUnread(358-361)markChannelAsUnread(363-372)message(291-293)user(100-102)channel(403-405)
Sources/StreamChat/Workers/ChannelUpdater.swift (6)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)lastReadMessageId(2264-2276)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
markUnread(515-522)
Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (1)
Sources/StreamChat/Repositories/MessageRepository.swift (2)
getMessage(304-338)getMessage(341-376)
Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId(2152-2154)
Sources/StreamChat/Repositories/ChannelRepository.swift (6)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)lastReadMessageId(2264-2276)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
loadChannelRead(374-376)message(291-293)Sources/StreamChat/Database/DTOs/MessageDTO.swift (5)
message(457-463)message(1369-1369)loadMessage(589-608)loadMessage(610-625)loadMessage(1587-1599)Sources/StreamChat/Database/DataStore.swift (1)
message(68-70)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (5)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
Sources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swift (3)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId(2152-2154)TestTools/StreamChatTestMockServer/MockServer/MockServerAttributes.swift (1)
userId(200-202)Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
encode(416-420)
Sources/StreamChat/Database/DatabaseSession.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
markChannelAsUnread(114-131)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
markChannelAsUnread(114-131)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (4)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (2)
Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (1)
event(208-210)Sources/StreamChat/WebSocketClient/Events/EventType.swift (1)
event(186-267)
Sources/StreamChat/Repositories/MessageRepository.swift (1)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (3)
loadMessage(589-608)loadMessage(610-625)loadMessage(1587-1599)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
Sources/StreamChat/Utils/Database/NSManagedObject+Extensions.swift (1)
fetch(181-199)
Sources/StreamChat/Workers/ReadStateHandler.swift (3)
Sources/StreamChat/Repositories/MessageRepository.swift (2)
getMessage(304-338)getMessage(341-376)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift (5)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (4)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)lastReadMessageId(2264-2276)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (4)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
markUnread(515-522)
Sources/StreamChat/StateLayer/Chat.swift (2)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (3)
Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (4)
Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
⏰ 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). (1)
- GitHub Check: Metrics
🔇 Additional comments (20)
StreamChat.xcodeproj/project.pbxproj (5)
321-322: PBXBuildFile entries forMarkUnreadPayload.swiftare consistent across targetsBoth build files (
4FC7B3F0and4FC7B3F1) reference the same fileRef and are each consumed in separate Sources lists later on, which matches the usual multi-target wiring pattern. No structural issues here.
3365-3365: PBXFileReference forMarkUnreadPayload.swiftis correctly definedThe new file reference uses the expected
sourcecode.swifttype and path, consistent with neighboring Swift files. This will let Xcode resolve the file cleanly.
5979-5983: Payloads group inclusion looks correct
MarkUnreadPayload.swiftis added under the existingPayloadsPBXGroup alongside other payload DTOs, which keeps project structure coherent.
11918-11918: Main target Sources phase wiring is correct
4FC7B3F1 /* MarkUnreadPayload.swift in Sources */is added to a Sources list with other core SDK files, so the main target will compile the new payload as expected.
13094-13094: Additional target Sources phase wiring is correct
4FC7B3F0 /* MarkUnreadPayload.swift in Sources */appears in another Sources list (likely a second target such as tests or a demo), which is the standard pattern for sharing the same file across targets.Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (2)
422-448: LGTM! Test updated correctly for new API.The existing test has been properly updated to use the new
MarkUnreadCriteria.messageIdwrapper while maintaining the same test logic.
450-478: Excellent test coverage for timestamp-based marking.The new test properly validates the timestamp-based unread marking functionality with a clear scenario: using a timestamp between two messages to retrieve the correct message.
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
363-372: LGTM! Mock updated correctly.The mock signature has been properly updated to use
MarkUnreadCriteriaand correctly forwards the parameter to the underlying session.Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
114-131: LGTM! Correctly wraps messageId in criteria enum.The call to
markChannelAsUnreadhas been properly updated to wrap themessageIdparameter in the newMarkUnreadCriteria.messageIdcase.DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (1)
466-483: LGTM! Demo action properly implements timestamp-based marking.The new demo action correctly prompts for a day offset, computes the date, and calls the new
markUnread(from: Date)API with appropriate error handling.Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (1)
601-604: LGTM! Fixes unread UI update for timestamp-based marking.This correctly handles
NotificationMarkUnreadEventby updating all unread-related UI components when the channel is marked unread via timestamp. This ensures the unread message separator and other UI elements are refreshed, addressing the issue mentioned in the PR description.TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
17-49: LGTM! Mock repository updated correctly.The mock has been properly updated to use
MarkUnreadCriteriaand stores the criteria for verification in tests.Sources/StreamChat/Workers/ReadStateHandler.swift (1)
48-94: LGTM! ReadStateHandler consistently updated.Both the completion-based and async variants of
markUnreadhave been properly updated to accept and forwardMarkUnreadCriteriathrough the call chain. The implementation correctly propagates the criteria to bothmessageRepositoryandchannelUpdater.Sources/StreamChat/Workers/ChannelUpdater.swift (1)
567-583: Updater–repository wiring for MarkUnreadCriteria looks goodThe
ChannelUpdater.markUnreadsignature and doc are correctly updated to takeMarkUnreadCriteriaand simply forward it toChannelRepository.markUnread, preserving behavior while enabling timestamp criteria.Sources/StreamChat/Repositories/ChannelRepository.swift (1)
61-103: Repository markUnread refactor is consistent with new payload/criteria typesThe repository correctly:
- accepts
from unreadCriteria: MarkUnreadCriteria,- builds the
MarkUnreadPayloadfor the endpoint, and- passes the same criteria into
session.markChannelAsUnread.Error propagation and the
ChannelNotCreatedYetfallback remain intact.Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (1)
316-424: Nice symmetry tests for partial unread when read/message are missingThe updated tests:
- switch existing partial-unread cases to
from: .messageId(...), and- add
*_messageTimestampvariants for “no read” and “no message” scenarios,which together validate that
markChannelAsUnreadbecomes a no-op (while still going through a single write) when either the read row or the target message cannot be resolved, for both criteria types.Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
272-279: Endpoint.markUnread now correctly uses the typed MarkUnreadPayloadSwitching
markUnreadto accept aMarkUnreadPayloadand using it as the request body keeps the HTTP contract (path/method) intact while aligning the endpoint with the new criteria/payload model and improving type safety at call sites.Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift (1)
452-494: Endpoint tests cover both message-id and timestamp mark-unread variants correctlyThe new
markUnreadendpoint tests look good: they useMarkUnreadPayloadwith.messageIdand.messageTimestampcriteria, assert fullAnyEndpointequality (path, method, requiresConnectionId, body), and confirm the expectedchannels/{type}/{id}/unreadpath. This gives solid coverage of the new payload-based API.Sources/StreamChat/Database/DatabaseSession.swift (1)
399-408: Channel read protocol update is consistent with criteria-based APISwitching
fromtoMarkUnreadCriteriaand updating the docstring to mention id or timestamp keeps the protocol aligned with the new unread model; no issues here.TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
103-109: Mock wiring for criteria-basedmarkUnreadlooks consistentTracking
markUnread_criteriainstead of a bare message id, resetting it incleanUp(), and assigning it in the overriddenmarkUnreadmatches the new production API and keeps the mock introspection aligned with test expectations.Also applies to: 250-256, 515-522
| if let event = event as? NotificationMarkUnreadEvent, let channel = channelController.channel, event.cid == channelController.cid, !messages.isEmpty { | ||
| updateAllUnreadMessagesRelatedComponents(channel: channel) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing mark unread message action is implemented in ChatChannelVC and it explicitly calls this method in the completion handler. Since I added demo app debug command then this did not get called and unread separator did not appear.
Should I clean up the explicit call? At the moment I did not touch it.
case is MarkUnreadActionItem:
dismiss(animated: true) { [weak self] in
self?.channelController.markUnread(from: message.id) { result in
if case let .success(channel) = result {
self?.updateAllUnreadMessagesRelatedComponents(channel: channel)
}
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's ok imo, we have one sample implementation (customers can change/decide which one to use).
Generated by 🚫 Danger |
SDK Performance
|
martinmitrevski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but the code coverage can be improved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
Tests/StreamChatTests/StateLayer/Chat_Tests.swift (1)
1311-1347: VerifylastMessageselection in new timestamp-based markUnread testThe new test correctly exercises
chat.markUnread(from: Date)and mirrors the ID-based variant, butlastMessageis initialized frommessages.first(same asfirstMessage), which is likely a copy-paste/semantic slip—messages.lastwould better represent the latest message and aligns with howlastMessageAtis set in themarkReadtest above. If this is unintentional, consider switching tomessages.lasthere (and in the existing ID-basedmarkUnreadtest) so the setup more realistically models "all messages read, then mark unread from the first message."Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4330-4408: Timestamp-based markUnread tests are good; consider adding one assertion on the criteria wiringThe new
markUnread(from: Date)tests nicely mirror the messageId variants for:
- channel not created
- read events disabled
- marking-as-read in progress
- no current user id
They validate surface behavior (error vs no error), but they don’t yet assert that the controller passes
MarkUnreadCriteria.messageTimestamp(...)through toChannelUpdater_Mock, or thatlastReadMessageIdis derived consistently with the messageId-based path.If you want tighter coverage of the new timestamp path, consider adding a positive test similar to
test_markUnread_whenIsNotMarkingAsRead_andCurrentUserIdIsPresent_whenThereAreOtherMessages_whenUpdaterSucceeds, but using a message’screatedAtas the timestamp and asserting:
updater.markUnread_criteria == .messageTimestamp(timestamp)updater.markUnread_lastReadMessageIdmatches the message immediately preceding that timestampThis would exercise the new enum case end-to-end without changing production code.
📜 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.
📒 Files selected for processing (2)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift(7 hunks)Tests/StreamChatTests/StateLayer/Chat_Tests.swift(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
Tests/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
🔇 Additional comments (1)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4135-4328: Mark-unread-by-messageId tests correctly updated to new API and criteria wiringThe updated tests now call
controller.markUnread(from: MessageId.unique)and validateupdater.markUnread_criteria == .messageId(messageId)in the success paths. This keeps existing behavior intact while exercising the newMarkUnreadCriteriaplumbing, and the scenarios (channel missing, feature disabled, current user present/absent, updater error/success) remain well covered. No issues here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
1345-1383: ClarifymarkUnread(from timestamp:)behavior and tighten documentationThe wiring into
readStateHandler.markUnread(from: .messageTimestamp(timestamp), in: channel)looks correct, but the doc comment is a bit too specific and may be misleading:
- The comment states that “If no message is found after the timestamp, the operation completes without error but no messages are marked as unread.” In practice, the call ultimately goes through
ChannelRepository.markUnread, which propagates backend errors directly to the caller. Given the PR objective notes that the backend may return errors when timestamps do not match any messages, this should be reflected here so integrators don’t assume a silent no-op.- The phrase “creation timestamp greater than to the provided timestamp” is grammatically off and also leaves ambiguity about whether the boundary is
>vs>=. It would be safer to say something like “starting from the first message whosecreatedAtis equal to or later than the given timestamp” (or whatever matches the actual backend semantics).I suggest updating the doc comment along these lines, for example:
- /// This method finds the first message with a creation timestamp greater than to the provided timestamp, - /// and marks all messages from that point forward as unread. If no message is found after the timestamp, - /// the operation completes without error but no messages are marked as unread. + /// This method marks messages as unread starting from the first message whose `createdAt` + /// is at or after the provided timestamp. If the timestamp does not correspond to any + /// message in the channel, the backend may return an error, which is propagated via + /// the `completion` result.Also,
markUnread(from messageId:)andmarkUnread(from timestamp:)share identical guard/error logic; if this grows further, consider a small private helper that takes aMarkUnreadCriteriato avoid duplication, though this is optional.Finally, since this is a new public API in
Sources/StreamChat, please ensure it’s also covered in the module’s migration notes and CHANGELOG entry for this release.
🧹 Nitpick comments (1)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4330-4408: Consider adding a success-path assertion for timestamp-basedMarkUnreadCriteriaThe new
markUnread(from: Date)tests nicely mirror the existing messageId flows for channel-not-created, read-events-disabled, and current-user/markRead combinations. To fully exercise the new timestamp-based API, consider adding at least one success-path test (similar to themessageIdvariants above) that:
- Uses a fixed
Datevalue (stored in a localtimestampvariable), and- Asserts that
env.channelUpdater?.markUnread_criteria(or the appropriate mock) is set to something like.messageTimestamp(timestamp)when the operation succeeds.This would align coverage between ID- and timestamp-based criteria and more directly guard the new enum case wiring.
📜 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.
📒 Files selected for processing (7)
CHANGELOG.md(1 hunks)DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift(1 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift(1 hunks)Sources/StreamChat/StateLayer/Chat.swift(1 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift(3 hunks)Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift(7 hunks)Tests/StreamChatTests/StateLayer/Chat_Tests.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- Tests/StreamChatTests/StateLayer/Chat_Tests.swift
- Sources/StreamChat/StateLayer/Chat.swift
- CHANGELOG.md
- DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift
🧰 Additional context used
📓 Path-based instructions (3)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Sources/StreamChat/Controllers/ChannelController/ChannelController.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
Sources/StreamChat/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Tests/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Prioritize backwards compatibility and API stability when changing code in the Stream iOS Chat SDK
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Sources/StreamChat/Controllers/ChannelController/ChannelController.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Sources/StreamChat/Controllers/ChannelController/ChannelController.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Applied to files:
Sources/StreamChat/Controllers/ChannelController/ChannelController.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Prioritize backwards compatibility and API stability when changing code in the Stream iOS Chat SDK
Applied to files:
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
🧬 Code graph analysis (3)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (4)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (4)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1314-1343)markUnread(1354-1383)messageId(2158-2160)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
markUnread(525-532)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (5)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
🔇 Additional comments (5)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (3)
105-111: Mark-unread mock now tracks fullMarkUnreadCriteriaUsing
markUnread_criteria: MarkUnreadCriteria?instead of a bare message ID keeps the mock aligned with the new API and lets tests assert both ID- and timestamp-based flows cleanly.
Based on learnings, this helps maintain high test coverage around the new unread behavior.
258-263: Clean-up correctly resetsmarkUnread_criteriaResetting
markUnread_criteriatonilalongside the other mark-unread fields avoids state leakage between tests and matches the existing cleanup pattern.
525-531: UpdatedmarkUnreadoverride matches criteria-based APIThe overridden
markUnreadnow acceptsfrom unreadCriteria: MarkUnreadCriteriaand records it intomarkUnread_criteria, mirroring the productionChannelUpdater/ChannelRepositorycontract and ensuring tests can verify the exact criteria passed through. Completion handling remains consistent with other mock methods.Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4135-4328: Mark-unread (messageId) tests correctly updated to useMarkUnreadCriteriaThe updated calls to
controller.markUnread(from: MessageId.unique)and the expectations onupdater.markUnread_criteria == .messageId(messageId)are consistent with the newMarkUnreadCriteriaAPI and keep existing behavior intact. No further changes needed here.Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
1335-1342: Mark-unread-by-messageId correctly delegates viaMarkUnreadCriteriaWrapping
messageIdin.messageId(messageId)when callingreadStateHandler.markUnreadcleanly aligns this API with the newMarkUnreadCriteriamodel and keeps behavior unchanged from the caller’s perspective. No issues here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift(2 hunks)Tests/StreamChatTests/StateLayer/Chat_Tests.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- Tests/StreamChatTests/StateLayer/Chat_Tests.swift
- Sources/StreamChat/Database/DTOs/MessageDTO.swift
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
Sources/StreamChat/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift
🧬 Code graph analysis (1)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (1)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (6)
message(457-463)message(1373-1373)loadMessage(593-612)loadMessage(614-629)loadMessage(1591-1603)countOtherUserMessages(558-578)
⏰ 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). (5)
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Test LLC (Debug)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Automated Code Review
- GitHub Check: Metrics
🔇 Additional comments (3)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (3)
141-141: LGTM! Signature change supports unified criteria handling.The parameter change from
messageIdtounreadCriteriaenables the method to handle both ID-based and timestamp-based unread marking, aligning with the PR's objective to introduceMarkUnreadCriteria.
157-158: The fallback defaults in lines 157-158 are correct and consistent with the codebase. Both.alwaysVisiblefordeletedMessagesVisibilityandfalseforshouldShowShadowedMessagesmatch the documented defaults inChatClientConfig(lines 181 and 185) and are used identically inMessageRepository.swift(lines 349-350) for the same nil-coalescing pattern. These defaults are also consistently validated across test files. For timestamp-based unread marking, these values are appropriate: showing deleted messages allows the operation to find the correct message boundary, and filtering shadowed messages prevents incorrect unread marking.
169-178: The asymmetric counting behavior is intentional and correct. The git history shows commitc33202a5titled "Exclude current message from unread count calculation," and the logic reflects the different semantics of the two criteria types: when marking unread by message ID, the message is included in the unread count; when marking by timestamp, the count includes only messages created after that timestamp. This distinction appropriately handles the two use cases.
| let findMessageDTO: () -> MessageDTO? = { | ||
| switch unreadCriteria { | ||
| case .messageId(let messageId): | ||
| return self.message(id: messageId) | ||
| case .messageTimestamp(let messageTimestamp): | ||
| let clientConfig = self.chatClientConfig | ||
| return try? MessageDTO.loadMessage( | ||
| beforeOrEqual: messageTimestamp, | ||
| cid: cid.rawValue, | ||
| deletedMessagesVisibility: clientConfig?.deletedMessagesVisibility ?? .alwaysVisible, | ||
| shouldShowShadowedMessages: clientConfig?.shouldShowShadowedMessages ?? false, | ||
| context: self | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log errors instead of silently discarding them.
The try? on line 154 silently swallows any errors thrown by MessageDTO.loadMessage. If the fetch fails due to a database error, context issue, or other problem, the function returns early at line 163 without any indication of what went wrong, making debugging difficult.
Apply this diff to log errors:
case .messageTimestamp(let messageTimestamp):
let clientConfig = self.chatClientConfig
- return try? MessageDTO.loadMessage(
- beforeOrEqual: messageTimestamp,
- cid: cid.rawValue,
- deletedMessagesVisibility: clientConfig?.deletedMessagesVisibility ?? .alwaysVisible,
- shouldShowShadowedMessages: clientConfig?.shouldShowShadowedMessages ?? false,
- context: self
- )
+ do {
+ return try MessageDTO.loadMessage(
+ beforeOrEqual: messageTimestamp,
+ cid: cid.rawValue,
+ deletedMessagesVisibility: clientConfig?.deletedMessagesVisibility ?? .alwaysVisible,
+ shouldShowShadowedMessages: clientConfig?.shouldShowShadowedMessages ?? false,
+ context: self
+ )
+ } catch {
+ log.error("Failed to load message by timestamp for marking unread: \(error)")
+ return nil
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let findMessageDTO: () -> MessageDTO? = { | |
| switch unreadCriteria { | |
| case .messageId(let messageId): | |
| return self.message(id: messageId) | |
| case .messageTimestamp(let messageTimestamp): | |
| let clientConfig = self.chatClientConfig | |
| return try? MessageDTO.loadMessage( | |
| beforeOrEqual: messageTimestamp, | |
| cid: cid.rawValue, | |
| deletedMessagesVisibility: clientConfig?.deletedMessagesVisibility ?? .alwaysVisible, | |
| shouldShowShadowedMessages: clientConfig?.shouldShowShadowedMessages ?? false, | |
| context: self | |
| ) | |
| } | |
| } | |
| let findMessageDTO: () -> MessageDTO? = { | |
| switch unreadCriteria { | |
| case .messageId(let messageId): | |
| return self.message(id: messageId) | |
| case .messageTimestamp(let messageTimestamp): | |
| let clientConfig = self.chatClientConfig | |
| do { | |
| return try MessageDTO.loadMessage( | |
| beforeOrEqual: messageTimestamp, | |
| cid: cid.rawValue, | |
| deletedMessagesVisibility: clientConfig?.deletedMessagesVisibility ?? .alwaysVisible, | |
| shouldShowShadowedMessages: clientConfig?.shouldShowShadowedMessages ?? false, | |
| context: self | |
| ) | |
| } catch { | |
| log.error("Failed to load message by timestamp for marking unread: \(error)") | |
| return nil | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift around lines 148 to
162, the use of `try?` when calling MessageDTO.loadMessage silently swallows
errors; replace it with a do-catch block that calls MessageDTO.loadMessage with
`try`, and in the catch log the caught error (using the existing logger in this
scope; if none exists, use a minimal fallback like print) including context
(cid/rawValue and the timestamp), then return nil to preserve current behavior.
Public Interface public class ChatChannelController: DataController, DelegateCallable, DataStoreProvider
- public func loadChannelReads(pagination: Pagination? = nil,completion: @escaping (Error?) -> Void)
+ public func markUnread(from timestamp: Date,completion: ((Result<ChatChannel, Error>) -> Void)? = nil)
- public func loadMoreChannelReads(limit: Int? = nil,completion: @escaping (Error?) -> Void)
+ public func loadChannelReads(pagination: Pagination? = nil,completion: @escaping (Error?) -> Void)
- public func enableSlowMode(cooldownDuration: Int,completion: ((Error?) -> Void)? = nil)
+ public func loadMoreChannelReads(limit: Int? = nil,completion: @escaping (Error?) -> Void)
- public func disableSlowMode(completion: ((Error?) -> Void)? = nil)
+ public func enableSlowMode(cooldownDuration: Int,completion: ((Error?) -> Void)? = nil)
- public func startWatching(isInRecoveryMode: Bool,completion: ((Error?) -> Void)? = nil)
+ public func disableSlowMode(completion: ((Error?) -> Void)? = nil)
- public func stopWatching(completion: ((Error?) -> Void)? = nil)
+ public func startWatching(isInRecoveryMode: Bool,completion: ((Error?) -> Void)? = nil)
- public func freezeChannel(completion: ((Error?) -> Void)? = nil)
+ public func stopWatching(completion: ((Error?) -> Void)? = nil)
- public func unfreezeChannel(completion: ((Error?) -> Void)? = nil)
+ public func freezeChannel(completion: ((Error?) -> Void)? = nil)
- public func pin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
+ public func unfreezeChannel(completion: ((Error?) -> Void)? = nil)
- public func unpin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
+ public func pin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
- public func uploadAttachment(localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil,completion: @escaping ((Result<UploadedAttachment, Error>) -> Void))
+ public func unpin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
- public func enrichUrl(_ url: URL,completion: @escaping (Result<LinkAttachmentPayload, Error>) -> Void)
+ public func uploadAttachment(localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil,completion: @escaping ((Result<UploadedAttachment, Error>) -> Void))
- public func loadPinnedMessages(pageSize: Int = .messagesPageSize,sorting: [Sorting<PinnedMessagesSortingKey>] = [],pagination: PinnedMessagesPagination? = nil,completion: @escaping (Result<[ChatMessage], Error>) -> Void)
+ public func enrichUrl(_ url: URL,completion: @escaping (Result<LinkAttachmentPayload, Error>) -> Void)
- public func currentCooldownTime()-> Int
+ public func loadPinnedMessages(pageSize: Int = .messagesPageSize,sorting: [Sorting<PinnedMessagesSortingKey>] = [],pagination: PinnedMessagesPagination? = nil,completion: @escaping (Result<[ChatMessage], Error>) -> Void)
- public func deleteFile(url: String,completion: ((Error?) -> Void)? = nil)
+ public func currentCooldownTime()-> Int
- public func deleteImage(url: String,completion: ((Error?) -> Void)? = nil)
+ public func deleteFile(url: String,completion: ((Error?) -> Void)? = nil)
- public func getFirstUnreadMessageId(for channel: ChatChannel)-> MessageId?
+ public func deleteImage(url: String,completion: ((Error?) -> Void)? = nil)
- public func setPushPreference(level: PushPreferenceLevel,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func getFirstUnreadMessageId(for channel: ChatChannel)-> MessageId?
- public func snoozePushNotifications(until date: Date,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func setPushPreference(level: PushPreferenceLevel,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func snoozePushNotifications(until date: Date,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
public class Chat
- @discardableResult public func reply(to parentMessageId: MessageId,text: String,showReplyInChannel: Bool = false,attachments: [AnyAttachmentPayload] = [],quote quotedMessageId: MessageId? = nil,mentions: [UserId] = [],pinning: MessagePinning? = nil,extraData: [String: RawJSON] = [:],silent: Bool = false,skipPushNotification: Bool = false,skipEnrichURL: Bool = false,messageId: MessageId? = nil)async throws -> ChatMessage
+ public func markUnread(from timestamp: Date)async throws
- @discardableResult public func loadReplies(for messageId: MessageId,pagination: MessagesPagination)async throws -> [ChatMessage]
+ @discardableResult public func reply(to parentMessageId: MessageId,text: String,showReplyInChannel: Bool = false,attachments: [AnyAttachmentPayload] = [],quote quotedMessageId: MessageId? = nil,mentions: [UserId] = [],pinning: MessagePinning? = nil,extraData: [String: RawJSON] = [:],silent: Bool = false,skipPushNotification: Bool = false,skipEnrichURL: Bool = false,messageId: MessageId? = nil)async throws -> ChatMessage
- public func loadReplies(before replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ @discardableResult public func loadReplies(for messageId: MessageId,pagination: MessagesPagination)async throws -> [ChatMessage]
- public func loadReplies(after replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(before replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadReplies(around replyId: MessageId,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(after replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadOlderReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(around replyId: MessageId,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadNewerReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadOlderReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
- @discardableResult public func translateMessage(_ messageId: MessageId,to language: TranslationLanguage)async throws -> ChatMessage
+ public func loadNewerReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func mute(expiration: Int? = nil)async throws
+ @discardableResult public func translateMessage(_ messageId: MessageId,to language: TranslationLanguage)async throws -> ChatMessage
- public func unmute()async throws
+ public func mute(expiration: Int? = nil)async throws
- public func hide(clearHistory: Bool = false)async throws
+ public func unmute()async throws
- public func show()async throws
+ public func hide(clearHistory: Bool = false)async throws
- public func pin(scope: ChannelPinningScope = .me)async throws
+ public func show()async throws
- public func unpin(scope: ChannelPinningScope = .me)async throws
+ public func pin(scope: ChannelPinningScope = .me)async throws
- public func subscribe(toEvent event: E.Type,handler: @escaping (E) -> Void)-> AnyCancellable
+ public func unpin(scope: ChannelPinningScope = .me)async throws
- public func subscribe(_ handler: @escaping (Event) -> Void)-> AnyCancellable
+ public func subscribe(toEvent event: E.Type,handler: @escaping (E) -> Void)-> AnyCancellable
- public func sendEvent(_ payload: EventPayload)async throws
+ public func subscribe(_ handler: @escaping (Event) -> Void)-> AnyCancellable
- public func enableSlowMode(cooldownDuration: Int)async throws
+ public func sendEvent(_ payload: EventPayload)async throws
- public func disableSlowMode()async throws
+ public func enableSlowMode(cooldownDuration: Int)async throws
- public func truncate(systemMessage: String? = nil,hardDelete: Bool = true,skipPush: Bool = false)async throws
+ public func disableSlowMode()async throws
- public func keystroke(parentMessageId: MessageId? = nil)async throws
+ public func truncate(systemMessage: String? = nil,hardDelete: Bool = true,skipPush: Bool = false)async throws
- public func stopTyping(parentMessageId: MessageId? = nil)async throws
+ public func keystroke(parentMessageId: MessageId? = nil)async throws
- public func update(name: String?,imageURL: URL?,team: String?,members: Set<UserId> = [],invites: Set<UserId> = [],filterTags: Set<String> = [],extraData: [String: RawJSON] = [:])async throws
+ public func stopTyping(parentMessageId: MessageId? = nil)async throws
- public func updatePartial(name: String? = nil,imageURL: URL? = nil,team: String? = nil,members: [UserId] = [],invites: [UserId] = [],filterTags: Set<String> = [],extraData: [String: RawJSON] = [:],unsetProperties: [String] = [])async throws
+ public func update(name: String?,imageURL: URL?,team: String?,members: Set<UserId> = [],invites: Set<UserId> = [],filterTags: Set<String> = [],extraData: [String: RawJSON] = [:])async throws
- public func deleteFile(at url: URL)async throws
+ public func updatePartial(name: String? = nil,imageURL: URL? = nil,team: String? = nil,members: [UserId] = [],invites: [UserId] = [],filterTags: Set<String> = [],extraData: [String: RawJSON] = [:],unsetProperties: [String] = [])async throws
- public func deleteImage(at url: URL)async throws
+ public func deleteFile(at url: URL)async throws
- public func uploadAttachment(with localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil)async throws -> UploadedAttachment
+ public func deleteImage(at url: URL)async throws
- @discardableResult public func loadWatchers(with pagination: Pagination)async throws -> [ChatUser]
+ public func uploadAttachment(with localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil)async throws -> UploadedAttachment
- @discardableResult public func loadMoreWatchers(limit: Int? = nil)async throws -> [ChatUser]
+ @discardableResult public func loadWatchers(with pagination: Pagination)async throws -> [ChatUser]
+ @discardableResult public func loadMoreWatchers(limit: Int? = nil)async throws -> [ChatUser] |
SDK Size
|
StreamChat XCSize
Show 7 more objects
|
StreamChatUI XCSize
|
|



🔗 Issue Links
Fixes: IOS-1277
🎯 Goal
Mark messages as unread using timestamp
📝 Summary
fromargument isDateChatChannelController.markUnread(from:completion:)Chat.markUnread(from:)MarkUnreadCriteriaenum for managing the difference of message id and message timestamp in existing methodsChatChannelVCnot updating the unread message separator (existing mark unread using a message id explicitly calls the update separator method)🛠 Implementation
Use the existing mark unread endpoint, just that we pass
message_timestampinstead ofmessage_id. Backend can throw errors when passing in random timestamps and there are no messages to match against.🎨 Showcase
🧪 Manual Testing Notes
☑️ Contributor Checklist
docs-contentrepoSummary by CodeRabbit
New Features
UI
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.