-
Notifications
You must be signed in to change notification settings - Fork 222
Add Message Reminders #3623
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
base: develop
Are you sure you want to change the base?
Add Message Reminders #3623
Conversation
Generated by 🚫 Danger |
SDK Size
|
SDK Performance
|
SDK Size
|
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 great so far 👍 I think the most important thing is to check how / when push notifications will be supported - the due feature doesn't make much sense without it.
Additionally, apart from docs, we need a notion doc for other SDKs.
DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
Outdated
Show resolved
Hide resolved
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.
Copilot reviewed 35 out of 55 changed files in this pull request and generated no comments.
Files not reviewed (20)
- DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift: Language not supported
- DemoApp/Screens/DemoAppTabBarController.swift: Language not supported
- DemoApp/Shared/DemoUsers.swift: Language not supported
- DemoApp/Shared/StreamChatWrapper.swift: Language not supported
- DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift: Language not supported
- DemoApp/StreamChat/Components/DemoChatMessageContentView.swift: Language not supported
- DemoApp/StreamChat/Components/DemoChatMessageLayoutOptionsResolver.swift: Language not supported
- DemoApp/StreamChat/DemoAppCoordinator+DemoApp.swift: Language not supported
- Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift: Language not supported
- Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift: Language not supported
- Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift: Language not supported
- Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift: Language not supported
- Sources/StreamChat/ChatClient+Environment.swift: Language not supported
- Sources/StreamChat/ChatClient.swift: Language not supported
- Sources/StreamChat/ChatClientFactory.swift: Language not supported
- Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift: Language not supported
- Sources/StreamChat/Controllers/MessageController/MessageController.swift: Language not supported
- Sources/StreamChat/Database/DTOs/MessageDTO.swift: Language not supported
- Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift: Language not supported
- Sources/StreamChat/Database/DatabaseSession.swift: Language not supported
60495fe
to
1eca9b2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🔭 Outside diff range comments (1)
Sources/StreamChat/Models/ChatMessage.swift (1)
272-325
:⚠️ Potential issueMissing reminder parameter in the
changing
method.The
changing
method doesn't include areminder
parameter, which is inconsistent with thereplacing
method and could lead to unexpected behavior when creating modified message instances.public func changing( text: String? = nil, type: MessageType? = nil, state: LocalMessageState? = nil, command: String? = nil, arguments: String? = nil, attachments: [AnyChatMessageAttachment]? = nil, translations: [TranslationLanguage: String]? = nil, originalLanguage: TranslationLanguage? = nil, moderationDetails: MessageModerationDetails? = nil, - extraData: [String: RawJSON]? = nil + extraData: [String: RawJSON]? = nil, + reminder: MessageReminderInfo? = nil ) -> ChatMessage { .init( // ... existing parameters ... textUpdatedAt: textUpdatedAt, - draftReply: draftReply + draftReply: draftReply, + reminder: reminder ?? self.reminder ) }
♻️ Duplicate comments (2)
Tests/StreamChatTests/Repositories/RemindersRepository_Tests.swift (2)
282-301
: Same response–enqueue race as above
See the earlier comment – reorder the call totest_mockResponseResult
before invokingupdateReminder
.
428-438
: Same response–enqueue race as above
Re-order the mocked response setup fordeleteReminder
.
🧹 Nitpick comments (20)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
69-71
: Consider adding error simulation capability.The
deleteReminder
method delegates correctly, but unlike some other mock methods, it doesn't callthrowErrorIfNeeded()
. Consider whether error simulation should be supported for testing error scenarios in reminder deletion.func deleteReminder(messageId: MessageId) { + try? throwErrorIfNeeded() underlyingSession.deleteReminder(messageId: messageId) }
Note: This would require changing the method signature to
throws
if error simulation is desired.DemoApp/Shared/DemoUsers.swift (1)
272-279
:❓ Verification inconclusive
Verify URL correctness and consider configuration alternatives.
The new
customBaseURL
property implementation looks structurally sound, but there are a couple of considerations:
URL Inconsistency: The URL contains "frankfurt-ce1" but this is for the Frankfurt C2 environment. Please verify this is the correct staging URL.
Hardcoded URLs: Consider whether staging URLs should be externally configurable rather than hardcoded, especially for security and maintainability reasons.
Please verify the staging URL is correct:
🌐 Web query:
Is https://chat-edge-frankfurt-ce1.stream-io-api.com/ the correct staging URL for Stream's Frankfurt C2 environment?
💡 Result:
Based on the available information, there is no direct confirmation in the provided sources regarding whether
https://chat-edge-frankfurt-ce1.stream-io-api.com/
is the correct staging URL for Stream's Frankfurt C2 environment. None of the search results specifically reference Stream staging or production environments, their corresponding URLs, or naming conventions.Given the URL structure, it appears to follow a naming convention that could be used for a Stream Chat edge node in Frankfurt ("frankfurt"), potentially in a "ce1" cluster or environment. The "C2" in your query may refer to a specific cluster or environment designation at Stream, but without documentation or a reliable reference in the search results, this cannot be confirmed.
Recommendation:
To verify the correct staging URL for Stream's Frankfurt C2 environment, consult:
- Stream's official developer documentation.
- The Stream team or your internal environment configuration documentation.
- Any onboarding or environment setup materials provided by Stream.
If you have access to your organization's environment configuration files, they may also reference the correct URL directly. If you want a definitive answer, you should reach out to Stream support or consult their environment documentation.
Citations:
- 1: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/LocationsOfEdgeServers.html
- 2: https://github.com/ripienaar/free-for-dev
- 3: https://cloud.google.com/vertex-ai/pricing
- 4: https://www.fraport.com/content/dam/fraport-company/documents/geschaeftsfelder/service/richtlinien-und-zahlungsbedingungen/richtlinien/en/online/C2.9%20Traffic%20Regulations.pdf/_jcr_content/renditions/original./C2.9%20Traffic%20Regulations.pdf
- 5: https://www.redbooks.ibm.com/redbooks/pdfs/sg248456.pdf
- 6: https://www-pub.iaea.org/MTCD/Publications/PDF/te_361_web.pdf
- 7: https://www.juniper.net/documentation/us/en/software/sd-cloud/sd-cloud/sd-cloud.pdf
- 8: https://www.bit-group.com
- 9: https://www.cancer.gov/types/lymphoma/hp/child-nhl-treatment-pdq
- 10: https://www.mizuhogroup.com/binaries/content/assets/pdf/emea/who-we-are/corporate-governance/mizuho-bank-europe-n.v/annual-reports/mizuho-bank-europe-nv---annual-report-2023.pdf
Confirm Frankfurt C2 Staging URL and Externalize Configuration
The
customBaseURL
for.frankfurtC2
currently points to
https://chat-edge-frankfurt-ce1.stream-io-api.com/
Please verify that this “ce1” cluster is indeed the correct staging endpoint for your Frankfurt C2 environment.File: DemoApp/Shared/DemoUsers.swift — Lines 272–279
• Ensure the URL matches the official Stream staging endpoint for Frankfurt C2 (consult Stream’s docs or internal environment-config sources).
• Consider moving these staging URLs into a configuration file or environment variables to improve maintainability and security.CHANGELOG.md (1)
12-15
: Fix list indentation for nested items
The sub-bullets under “Add Message Reminders” are indented by 3 spaces, but the rest of the changelog uses 2 spaces. Adjust to maintain consistency and satisfy markdownlint’s MD007 rule.- - Add `ChatMessageController.createReminder()` + - Add `ChatMessageController.createReminder()` - - Add `ChatMessageController.updateReminder()` + - Add `ChatMessageController.updateReminder()` - - Add `ChatMessageController.deleteReminder()` + - Add `ChatMessageController.deleteReminder()` - - Add `MessageReminderListController` and `MessageReminderListQuery` + - Add `MessageReminderListController` and `MessageReminderListQuery`🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
12-12: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
Sources/StreamChat/APIClient/Endpoints/ReminderEndpoints.swift (1)
8-9
: Clarify the comment for createReminder method.The comment states "Creates or updates a reminder" but there's a separate
updateReminder
method. This could be confusing for API consumers.- // Creates or updates a reminder for a message + // Creates a new reminder for a messageSources/StreamChat/APIClient/Endpoints/Payloads/ReminderPayloads.swift (1)
8-44
: Consider using struct instead of class for ReminderPayload.Since
ReminderPayload
represents immutable data and doesn't require reference semantics, astruct
would be more appropriate than aclass
. This applies to the other payload types as well.-class ReminderPayload: Decodable { +struct ReminderPayload: Decodable {Structs provide value semantics, better performance for small data models, and are more idiomatic in Swift for data transfer objects.
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/RemindersRepository_Mock.swift (1)
121-121
: Ensure consistent error handling pattern.The
deleteReminder
method immediately calls the completion with the preset error, while other methods check for preset results first. Consider making this pattern consistent across all methods.Apply this diff for consistency:
- completion(deleteReminder_error) + if let error = deleteReminder_error { + completion(error) + } else { + completion(nil) + }DemoApp/Screens/DemoAppTabBarController.swift (3)
17-18
: Unused property should be removed or utilized.The
eventsController
property is declared but never used in the implementation. Either remove it or implement the intended functionality.- // Events controller for listening to chat events - private var eventsController: EventsController!
109-112
: Consider caching reminder count for performance.The badge update method accesses
allRemindersListController.reminders
which might trigger database queries. Consider caching the count if this becomes a performance concern.private func updateRemindersBadge() { - let reminders = allRemindersListController.reminders - reminderListVC.tabBarItem.badgeValue = reminders.isEmpty ? nil : "\(reminders.count)" + let reminderCount = allRemindersListController.reminders.count + reminderListVC.tabBarItem.badgeValue = reminderCount > 0 ? "\(reminderCount)" : nil }
17-18
: Consider removing or utilizing the events controller property.The
eventsController
property is declared but doesn't appear to be used in the constructor or elsewhere in the visible code. This might be leftover code or incomplete implementation.If this property is not used, consider removing it:
- // Events controller for listening to chat events - private var eventsController: EventsController!If it's needed for future implementation, add a TODO comment explaining its purpose.
DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (1)
117-151
: Localise & DRY alert-action titlesHard-coded English strings and duplicated blocks make localisation/error-prone maintenance hard.
Consider extracting a helper that builds the preset list or usingString.localizedStringWithFormat
.Sources/StreamChat/WebSocketClient/Events/ReminderEvents.swift (1)
28-33
:payload
property is dead code
ReminderCreated/Updated/Deleted/Due …DTO
store the fullEventPayload
in an unusedpayload
var.
If it’s only needed insideinit
, drop the property to save memory and silence warnings.DemoApp/Screens/DemoReminderListVC.swift (2)
387-395
: Same for pagination – expose failures
loadMoreReminders
swallows the pagination error; consider updating UI or revertingisPaginatingReminders
only on success.
703-738
: Rounding logic can under-report 59-second intervals
ceil(timeInterval / 60 - 0.5)
is clever but produces 0 whentimeInterval
< 30 s.
Usemax(1, Int((timeInterval / 60).rounded()))
to guarantee a minimum of “1 min”.Tests/StreamChatTests/Repositories/RemindersRepository_Tests.swift (1)
267-268
: Remove unused localmessageDTO
messageDTO
is never referenced. Xcode will warn and some linters will fail CI.- let messageDTO = try XCTUnwrap(database.viewContext.message(id: messageId))
Simply delete the line.
Sources/StreamChat/Repositories/RemindersRepository.swift (1)
114-118
:cid
parameter is unused inupdateReminder
cid
is accepted but never referenced, which may confuse API users and invites drift between signature and implementation.
Either remove it or use it (e.g. for extra validation/logging).-func updateReminder( - messageId: MessageId, - cid: ChannelId, +func updateReminder( + messageId: MessageId, remindAt: Date?, completion: @escaping ((Result<MessageReminder, Error>) -> Void) )(and update call-sites accordingly).
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (2)
22-29
:willSave()
mutates every time even for unchanged objectsBecause
willSave
is invoked on every save cycle for all registered objects, touchingsortingRemindAt
when the underlying value hasn’t changed still marks the DTO as dirty and forces Core Data to issue an UPDATE.
You already computenewSortingRemindAt
; simply bail out early if nothing changes to avoid unnecessary SQL traffic:- let newSortingRemindAt = remindAt ?? .distantFuture.bridgeDate - if sortingRemindAt != newSortingRemindAt { - sortingRemindAt = newSortingRemindAt - } + let newSortingRemindAt = remindAt ?? .distantFuture.bridgeDate + guard sortingRemindAt != newSortingRemindAt else { return } + sortingRemindAt = newSortingRemindAt
146-153
: No error propagation when the reminder is missing
deleteReminder(messageId:)
just returns when no reminder is found, swallowing a potential inconsistency (e.g. local DB drift).
Consider surfacing an error or at least logging a warning so the caller can react.- guard let reminderDTO = message?.reminder else { - return - } + guard let reminderDTO = message?.reminder else { + log.warning("Attempted to delete a reminder that does not exist for messageId: \(messageId)") + return + }Sources/StreamChat/Query/MessageReminderListQuery.swift (1)
97-104
: Guard against invalid page sizesThe public initializer accepts any
pageSize
. Negative or zero values will hit the backend and probably fail. A small clamp prevents misuse:- pageSize: Int = 25, + pageSize: Int = 25,let safeSize = max(1, pageSize) pagination = Pagination(pageSize: safeSize, cursor: next)Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController.swift (1)
121-137
:synchronize
does not clear pagination state on subsequent callsIf a caller re-invokes
synchronize()
after loading more pages,nextCursor
andhasLoadedAllReminders
keep their old values, which can short-circuit further pagination.Reset them before the network call:
+ nextCursor = nil + hasLoadedAllReminders = false remindersRepository.loadReminders(query: query) { [weak self] result inDemoApp/StreamChat/Components/DemoChatMessageContentView.swift (1)
30-37
: Consider localizing the hardcoded text string.The label text "Saved for later" is hardcoded, which could cause localization issues in international deployments.
Apply this diff to use localized strings:
- label.text = "Saved for later" + label.text = L10n.Message.savedForLaterAlternatively, if localization constants aren't available yet, consider adding a comment indicating this needs localization:
label.text = "Saved for later" + // TODO: Localize this string
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (61)
CHANGELOG.md
(1 hunks)DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift
(6 hunks)DemoApp/Screens/DemoAppTabBarController.swift
(3 hunks)DemoApp/Screens/DemoReminderListVC.swift
(1 hunks)DemoApp/Shared/DemoUsers.swift
(1 hunks)DemoApp/Shared/StreamChatWrapper.swift
(1 hunks)DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift
(3 hunks)DemoApp/StreamChat/Components/DemoChatMessageContentView.swift
(2 hunks)DemoApp/StreamChat/Components/DemoChatMessageLayoutOptionsResolver.swift
(2 hunks)DemoApp/StreamChat/DemoAppCoordinator+DemoApp.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
(3 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
(5 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/ReminderPayloads.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/ReminderEndpoints.swift
(1 hunks)Sources/StreamChat/ChatClient+Environment.swift
(1 hunks)Sources/StreamChat/ChatClient.swift
(1 hunks)Sources/StreamChat/ChatClientFactory.swift
(1 hunks)Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
(3 hunks)Sources/StreamChat/Controllers/MessageController/MessageController.swift
(3 hunks)Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController+Combine.swift
(1 hunks)Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController.swift
(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift
(3 hunks)Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift
(1 hunks)Sources/StreamChat/Database/DatabaseSession.swift
(2 hunks)Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents
(3 hunks)Sources/StreamChat/Models/ChatMessage.swift
(5 hunks)Sources/StreamChat/Models/DraftMessage.swift
(1 hunks)Sources/StreamChat/Models/MessageReminder.swift
(1 hunks)Sources/StreamChat/Query/Filter+predicate.swift
(2 hunks)Sources/StreamChat/Query/Filter.swift
(1 hunks)Sources/StreamChat/Query/MessageReminderListQuery.swift
(1 hunks)Sources/StreamChat/Repositories/RemindersRepository.swift
(1 hunks)Sources/StreamChat/WebSocketClient/EventMiddlewares/ReminderUpdaterMiddleware.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/WebSocketClient/Events/ReminderEvents.swift
(1 hunks)StreamChat.xcodeproj/project.pbxproj
(63 hunks)TestTools/StreamChatTestTools/Extensions/Unique/ChatMessage+Unique.swift
(1 hunks)TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderCreated.json
(1 hunks)TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderDeleted.json
(1 hunks)TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderDue.json
(1 hunks)TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderUpdated.json
(1 hunks)TestTools/StreamChatTestTools/Fixtures/JSONs/ReminderPayload.json
(1 hunks)TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift
(2 hunks)TestTools/StreamChatTestTools/Mocks/Models + Extensions/MessageReminder_Mock.swift
(1 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/ChatClient_Mock.swift
(3 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
(1 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/RemindersRepository_Mock.swift
(1 hunks)Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
(2 hunks)Tests/StreamChatTests/APIClient/Endpoints/Payloads/ReminderPayloads_Tests.swift
(1 hunks)Tests/StreamChatTests/APIClient/Endpoints/ReminderEndpoints_Tests.swift
(1 hunks)Tests/StreamChatTests/Controllers/MessageController/MessageController+Reminders_Tests.swift
(1 hunks)Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController+Combine_Tests.swift
(1 hunks)Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController_Tests.swift
(1 hunks)Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift
(1 hunks)Tests/StreamChatTests/Query/MessageReminderListQuery_Tests.swift
(1 hunks)Tests/StreamChatTests/Repositories/RemindersRepository_Tests.swift
(1 hunks)Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ReminderUpdaterMiddleware_Tests.swift
(1 hunks)Tests/StreamChatTests/WebSocketClient/Events/ReminderEvents_Tests.swift
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift (2)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
saveReminder
(65-67)message
(281-283)TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)
Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (3)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId
(1912-1914)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
pollVotes
(597-599)Sources/StreamChat/Database/DTOs/PollVoteDTO.swift (1)
pollVotes
(214-219)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/MessageReminder_Mock.swift (2)
Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController_Tests.swift (1)
mock
(261-277)TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)
Tests/StreamChatTests/Controllers/MessageController/MessageController+Reminders_Tests.swift (5)
TestTools/StreamChatTestTools/Mocks/StreamChat/ChatClient_Mock.swift (4)
mock
(128-161)mock
(293-307)mock
(311-325)cleanUp
(101-116)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/RemindersRepository_Mock.swift (4)
cleanUp
(44-65)createReminder
(80-94)updateReminder
(96-110)deleteReminder
(112-122)TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (1)
mockDMChannel
(130-184)Sources/StreamChat/Repositories/RemindersRepository.swift (3)
createReminder
(63-105)updateReminder
(113-157)deleteReminder
(164-220)Sources/StreamChat/Controllers/MessageController/MessageController.swift (3)
createReminder
(945-958)updateReminder
(965-978)deleteReminder
(983-994)
Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController+Combine.swift (3)
DemoApp/Screens/DemoReminderListVC.swift (1)
controller
(471-478)DemoApp/Screens/DemoAppTabBarController.swift (1)
controller
(102-107)Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController_Tests.swift (1)
controller
(174-181)
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/RemindersRepository_Mock.swift (2)
Sources/StreamChat/Repositories/RemindersRepository.swift (4)
loadReminders
(34-55)createReminder
(63-105)updateReminder
(113-157)deleteReminder
(164-220)Sources/StreamChat/Controllers/MessageController/MessageController.swift (3)
createReminder
(945-958)updateReminder
(965-978)deleteReminder
(983-994)
Tests/StreamChatTests/Repositories/RemindersRepository_Tests.swift (4)
Sources/StreamChat/Repositories/RemindersRepository.swift (4)
loadReminders
(34-55)createReminder
(63-105)updateReminder
(113-157)deleteReminder
(164-220)TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (2)
test_simulateResponse
(108-111)test_mockResponseResult
(118-120)TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift (1)
writeSynchronously
(175-182)
DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (1)
Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift (1)
chatMessageActionsVCDidFinish
(894-897)
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
12-12: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Automated Code Review
- GitHub Check: Test LLC (Debug)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Metrics
🔇 Additional comments (125)
Sources/StreamChat/ChatClientFactory.swift (1)
129-129
: LGTM! Proper integration of reminder middleware.The addition of
ReminderUpdaterMiddleware()
correctly integrates the reminder event handling into the event processing pipeline. The positioning betweenDraftUpdaterMiddleware()
andUserTypingStateUpdaterMiddleware()
follows the established pattern for updater middlewares.Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift (1)
297-298
: LGTM! Consistent model property initialization.The addition of
reminder: nil
alongside the existingdraftReply: nil
correctly maintains consistency with the updatedChatMessage
model structure. Settingreminder
tonil
is appropriate for this workaround method that handles deleted messages, as deleted messages wouldn't have active reminders.TestTools/StreamChatTestTools/Extensions/Unique/ChatMessage+Unique.swift (1)
56-57
: LGTM! Proper test utility alignment with model changes.The addition of
reminder: nil
alongsidedraftReply: nil
correctly updates the test utility to match the updatedChatMessage
model structure. This ensures that test messages can properly represent the new reminder state while providing a clean baseline for testing scenarios.Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1)
18-18
: LGTM! Appropriate offline queuing behavior for reminder endpoints.The addition of
.reminders, .reminder
to the list of endpoints that should not be queued offline is correct. Reminder operations require real-time server interaction for proper scheduling and notification handling, making offline queuing inappropriate. This follows the established pattern for other endpoints requiring immediate server communication.Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift (1)
438-447
: LGTM! Test data setup correctly extended for reminders feature.The addition of reminder creation in the test data setup follows the established pattern and properly supports testing the new message reminders functionality. The payload structure with unique timestamps for
remindAt
,createdAt
, andupdatedAt
creates realistic test scenarios.DemoApp/Shared/StreamChatWrapper.swift (1)
41-44
: LGTM! Clean implementation of custom base URL support.The code properly uses optional chaining to safely extract and apply custom base URLs from
DemoApiKeys
. The placement in the initialization sequence is logical, and the implementation supports the Frankfurt C2 environment configuration mentioned in the PR objectives.TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderDue.json (1)
1-14
: LGTM! Well-structured JSON fixture for reminder due events.This fixture provides comprehensive test data for reminder due notifications with:
- Proper ISO timestamp formatting
- Realistic UUIDs and channel identifiers
- Complete reminder object structure with all required fields
- Consistent user IDs across the payload
The fixture correctly supports testing the new reminder due event handling functionality.
Sources/StreamChat/Query/Filter.swift (1)
430-439
: LGTM! Excellent addition of nil filtering convenience method.The
isNil
method is correctly implemented using the.exists
operator withfalse
value, which is the standard approach for checking non-existence. The implementation:
- Follows the established pattern of other filter methods
- Properly utilizes the key's
valueMapper
andkeyPathString
properties- Includes clear documentation
- Provides a useful convenience for filtering nil values in the reminders feature
Sources/StreamChat/Models/DraftMessage.swift (1)
165-165
: LGTM! Consistent with established pattern.The explicit reset of the
reminder
property tonil
when creating aChatMessage
from aDraftMessage
is correct and follows the same pattern as other properties (poll
,textUpdatedAt
,draftReply
). This ensures that draft messages don't carry reminder state when converted to regular messages.TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (2)
53-54
: LGTM! Proper mock parameter addition.The addition of the
reminder
parameter to the mock factory method follows the established pattern of other optional parameters with appropriate default values. This enables comprehensive testing of reminder functionality.
95-96
: LGTM! Correct parameter forwarding.The parameters are properly forwarded to the
ChatMessage
initializer, maintaining consistency with the mock method signature.Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (3)
58-60
: LGTM! Proper endpoint case addition.The new reminder endpoint cases follow the established naming convention and pattern. The addition of both general
reminders
and message-specificreminder(MessageId)
cases provides the necessary API endpoints for the reminder feature.
142-143
: LGTM! Consistent URL path mapping.The URL path mappings are consistent with existing patterns:
reminders
→"reminders/query"
follows the query patternreminder(MessageId)
→"messages/\(messageId)/reminders"
follows the message-specific resource pattern
161-161
: LGTM! Minor reorganization.The
pollVotes
case appears to have been reordered in the switch statement, which doesn't affect functionality and may improve code organization.Sources/StreamChat/ChatClient+Environment.swift (1)
149-154
: LGTM! Follows established dependency injection pattern.The
remindersRepositoryBuilder
addition perfectly follows the established pattern of other repository builders in the environment:
- Consistent parameter types (
DatabaseContainer
,APIClient
)- Proper closure structure and initialization
- Enables clean dependency injection for the reminder feature
TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderUpdated.json (1)
1-15
: Well-structured test fixture for reminder updated events.The JSON structure is consistent with webhook event patterns and includes all necessary fields with appropriate data types. The test data is realistic and properly formatted.
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (4)
88-89
: Good code organization with MARK comments.Adding section markers improves code navigation and readability.
115-116
: Proper section organization for initialization code.The MARK comment clearly delineates the initialization section.
127-127
: Essential super.init() call for proper inheritance.The super.init() call is necessary to ensure proper superclass initialization in the inheritance hierarchy.
130-130
: Minor formatting improvement in comment.Using straight apostrophe improves comment consistency.
TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderDeleted.json (1)
1-15
: Consistent test fixture for reminder deleted events.The structure matches the reminder updated fixture appropriately, with only the event type differing. This provides good test coverage for reminder deletion scenarios.
DemoApp/StreamChat/Components/DemoChatMessageLayoutOptionsResolver.swift (2)
11-11
: New layout option properly defined.The saveForLaterInfo option follows the same pattern as the existing pinInfo option.
32-35
: Correct implementation of conditional layout option logic.The logic appropriately checks both the feature flag (
isRemindersEnabled
) and message state (reminder != nil
) before adding the layout option. This follows the same pattern as the existing pinInfo implementation and ensures proper feature gating.Sources/StreamChat/ChatClient.swift (2)
81-84
: LGTM: Consistent repository initialization pattern.The change from non-lazy constant to lazy variable for
draftMessagesRepository
aligns with the environment-based dependency injection pattern used throughout the codebase. This maintains architectural consistency with other repository initializations.
86-89
: LGTM: Proper integration of reminders repository.The
remindersRepository
follows the established lazy initialization pattern using the environment builder. This integration is consistent with the existing repository architecture and supports the new message reminders feature effectively.TestTools/StreamChatTestTools/Mocks/Models + Extensions/MessageReminder_Mock.swift (1)
8-26
: LGTM: Well-structured mock implementation.The mock factory method follows established patterns with appropriate default values. The use of
.unique
,.mock()
, and date initialization is consistent with other mock implementations in the codebase.TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
65-67
: LGTM: Proper mock delegation for saveReminder.The
saveReminder
method correctly delegates to the underlying session with all required parameters and proper return type handling.Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (2)
69-72
: LGTM: Comprehensive test coverage for reminder endpoints.The test properly verifies that both reminder endpoint cases correctly return
false
forshouldBeQueuedOffline
, which aligns with the expectation that reminder operations should not be queued for offline execution.
154-156
: LGTM: Proper encoding/decoding test coverage.The additions to the Codable test ensure that the new reminder endpoints are properly tested for encoding and decoding functionality, maintaining consistency with other endpoint tests.
DemoApp/Shared/DemoUsers.swift (2)
260-260
: LGTM! Consistent with existing pattern.The new API key case follows the same pattern as existing entries and includes a helpful comment identifying it as Frankfurt C2 Staging.
266-266
: LGTM! Consistent app name mapping.The app name mapping for the new Frankfurt C2 case is consistent with the existing pattern and provides a clear, descriptive name.
CHANGELOG.md (2)
10-10
: Documenting new filter
The newFilter.isNil
entry clearly conveys its purpose. Please ensure that the corresponding API reference and usage examples are updated to include this filter.
11-11
: Introduce Message Reminders feature
The “Add Message Reminders” entry succinctly summarizes the new capability. Good call on linking to the PR for context.DemoApp/StreamChat/DemoAppCoordinator+DemoApp.swift (1)
61-77
: LGTM! Consistent integration pattern.The reminder list VC integration follows the exact same pattern as the existing thread and draft controllers, maintaining consistency in the codebase. The logout/disconnect closure setup and navigation controller wrapping are properly implemented.
Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift (4)
57-57
: LGTM! Proper coding key addition.The reminder coding key is correctly added to the MessagePayloadsCodingKeys enum following the established pattern.
116-116
: LGTM! Consistent property declaration.The reminder property follows the same optional pattern as other similar properties like
draft
andpoll
.
183-183
: LGTM! Proper conditional decoding.The reminder decoding follows the established pattern for optional properties using
decodeIfPresent
.
224-225
: LGTM! Complete initializer integration.The reminder parameter and property assignment are properly integrated into the convenience initializer, maintaining consistency with other optional properties.
Also applies to: 265-265
Tests/StreamChatTests/Query/MessageReminderListQuery_Tests.swift (4)
10-19
: LGTM! Comprehensive default initialization test.The test properly verifies all default values including filter (nil), pagination (pageSize: 25, offset: 0), and default sorting (remindAt, ascending).
21-36
: LGTM! Thorough custom initialization test.The test validates custom filter, sort, and pageSize parameters are properly applied and accessible through the query object.
38-97
: LGTM! Comprehensive encoding test coverage.The encoding tests cover all important scenarios: with all fields, without filter, and without sort. The use of
AssertJSONEqual
is appropriate for JSON comparison testing.
99-111
: LGTM! Important key validation tests.Testing the raw values of filter and sorting keys ensures the API contract is maintained and prevents accidental changes to the string values used in network requests.
Sources/StreamChat/Database/DatabaseSession.swift (2)
545-560
: LGTM! Well-designed protocol following established patterns.The
ReminderDatabaseSession
protocol follows the same design patterns as other database session protocols with proper documentation, parameter types, error handling, and return value annotations.
689-689
: LGTM! Proper protocol composition.The
ReminderDatabaseSession
is correctly integrated into the mainDatabaseSession
protocol following the established pattern used by other specialized database session protocols.Sources/StreamChat/WebSocketClient/EventMiddlewares/ReminderUpdaterMiddleware.swift (1)
7-42
: LGTM! Well-structured event middleware implementation.The middleware correctly follows the established pattern for EventMiddleware implementations. The switch statement appropriately handles all reminder-related events, includes proper error handling with logging, and maintains consistency with the existing codebase architecture.
Sources/StreamChat/Database/DTOs/MessageDTO.swift (3)
91-91
: LGTM! Proper Core Data property declaration.The optional relationship to
MessageReminderDTO
is correctly declared following Core Data conventions.
1045-1050
: LGTM! Robust reminder handling logic.The implementation correctly handles both reminder creation and deletion scenarios:
- Creates/updates reminder when present in payload
- Cleans up existing reminder when not present in payload
- Includes proper error handling with try-catch
1798-1803
: LGTM! Proper domain model mapping.The reminder mapping correctly transforms the Core Data DTO to the domain model, appropriately handling optional dates with
bridgeDate
conversion and using optional mapping with.map
.Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (3)
78-78
: LGTM! Properly defined channel-to-reminders relationship.The to-many relationship from
ChannelDTO
toMessageReminderDTO
with Nullify deletion rule is correctly implemented, ensuring reminders are not automatically deleted when a channel is removed.
265-265
: LGTM! Properly defined message-to-reminder relationship.The to-one relationship from
MessageDTO
toMessageReminderDTO
with Nullify deletion rule is correctly implemented, ensuring the reminder is not automatically deleted when a message is removed.
324-335
: Well-designed MessageReminderDTO entity with proper optimization.The entity definition includes:
- Appropriate attributes for reminder functionality (
createdAt
,remindAt
,sortingRemindAt
)- Proper relationships to both channel and message entities
- Performance optimization with fetch index on
id
attributeThe
sortingRemindAt
attribute suggests good consideration for query performance in reminder lists.TestTools/StreamChatTestTools/Fixtures/JSONs/ReminderPayload.json (1)
1-113
: Comprehensive and well-structured test fixture.The reminder payload fixture includes:
- Complete channel configuration with
user_message_reminders: true
enabled- Full message payload with user details
- Proper timestamp formatting and realistic test data
remind_at: null
which correctly represents "save for later" functionalityThis fixture will effectively support testing of reminder payload decoding and processing.
TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ReminderCreated.json (1)
1-105
: Well-structured reminder created event fixture.The event fixture correctly represents a
reminder.created
event with:
- Proper event metadata (
created_at
,message_id
,type
,user_id
)- Complete nested reminder payload with channel, message, and user details
- Consistent structure matching the EventPayload expectations
remind_at: null
appropriately representing save-for-later remindersThis fixture will effectively test the reminder event handling pipeline.
Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (5)
43-43
: Consistent addition to CodingKeys enum.The
reminder
case is properly added to the CodingKeys enum, following the established pattern for other payload fields.
81-81
: Properly declared reminder property.The
reminder: ReminderPayload?
property is correctly typed as optional, allowing events to include or exclude reminder data as needed.
114-115
: Initializer properly updated with reminder parameter.The initializer signature correctly includes the reminder parameter with a default value of
nil
, maintaining backward compatibility while supporting reminder events.
148-148
: Proper assignment in initializer.The reminder property is correctly assigned in the initializer, following the established pattern for other properties.
186-186
: Correct reminder decoding implementation.The reminder field is properly decoded using
decodeIfPresent
, which appropriately handles cases where reminder data may or may not be present in the event payload.DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift (3)
25-26
: LGTM! Reminder configuration property added correctly.The new
isRemindersEnabled
property follows the same pattern as other feature flags in the configuration.
350-353
: LGTM! UI configuration handling is consistent.The switch button configuration for reminders follows the same pattern as other feature toggles.
751-753
: LGTM! Base URL update logic is correct.The logic properly updates the chat client config base URL when an environment with a custom base URL is selected.
Sources/StreamChat/Models/ChatMessage.swift (3)
70-71
: LGTM! Reminder property added correctly.The optional
MessageReminderInfo?
property is properly documented and follows the existing code patterns.
223-224
: LGTM! Initializer parameter added correctly.The reminder parameter is properly added to the initializer signature.
568-568
: LGTM! Equality comparison updated correctly.The reminder property is properly included in the equality check, ensuring message equality accounts for reminder state.
TestTools/StreamChatTestTools/Mocks/StreamChat/ChatClient_Mock.swift (2)
155-155
: LGTM! Mock reminders repository integration is correct.The
remindersRepositoryBuilder
is properly added to the mock environment, following the same pattern as other repository builders.
213-215
: LGTM! Mock repository accessor follows established pattern.The computed property provides typed access to the mock reminders repository, consistent with other repository accessors in the extension.
Tests/StreamChatTests/APIClient/Endpoints/ReminderEndpoints_Tests.swift (4)
9-28
: LGTM! Create reminder endpoint test is comprehensive.The test properly validates the endpoint construction for creating reminders, including HTTP method, path, body, and URL structure.
30-48
: LGTM! Update reminder endpoint test follows consistent pattern.The test correctly validates the PATCH method and endpoint structure for updating reminders.
50-66
: LGTM! Delete reminder endpoint test is well-structured.The test properly validates the DELETE method with no body for reminder deletion.
68-88
: LGTM! Query reminders endpoint test covers complex query structure.The test validates the POST method for querying reminders with filter, sort, and pagination parameters, providing comprehensive coverage.
Sources/StreamChat/APIClient/Endpoints/ReminderEndpoints.swift (2)
42-50
: Consider the HTTP method choice for queryReminders.Using POST for a query operation is unusual - typically GET would be used. However, this might be intentional if the query body is complex or contains sensitive data that shouldn't be in URL parameters.
Could you verify if POST is the intended HTTP method for the reminders query endpoint based on the API specification? If the query parameters are simple, consider using GET with query parameters instead.
7-51
: Well-structured endpoint definitions.The endpoint implementations follow good practices with proper HTTP methods, consistent structure, and appropriate response types. The use of
requiresConnectionId: false
is correct for REST endpoints.Sources/StreamChat/APIClient/Endpoints/Payloads/ReminderPayloads.swift (2)
47-59
: Clean and focused request body design.The
ReminderRequestBody
is well-designed with a single responsibility and proper encoding setup.
62-79
: Good pagination and response wrapper design.Both
RemindersQueryPayload
andReminderResponsePayload
follow good API design patterns with proper pagination support and consistent response wrapping.Sources/StreamChat/Models/MessageReminder.swift (2)
8-53
: Excellent domain model design.The
MessageReminder
struct is well-designed with:
- Clear separation of concerns using internal initializer for encapsulation
- Proper
Hashable
implementation using only the uniqueid
- Good documentation explaining the purpose of each property
- Appropriate use of optional
remindAt
for bookmarks vs timed reminders
55-75
: Smart design to avoid circular dependencies.The
MessageReminderInfo
struct is a clever solution to provide reminder metadata without creating circular references betweenChatMessage
andMessageReminder
. This design pattern enables clean embedding of reminder data in message models.Sources/StreamChat/Query/Filter+predicate.swift (2)
7-13
: Good refactoring to enable broader filter usage.Moving the filter predicate functionality from a channel-specific extension to a general
Filter
extension is a smart refactoring that enables reuse for the new reminder filtering system while maintaining the same logic.
207-226
: Properly generalized utility properties.The
mappedValue
andmappedArrayValue
properties have been correctly moved to the general extension, maintaining their functionality while making them available for other filter scopes like reminders.Sources/StreamChat/Controllers/MessageController/MessageController.swift (3)
193-194
: LGTM! Clean property addition.The
remindersRepository
property follows the established pattern for repository dependencies in this controller.
216-216
: LGTM! Proper dependency injection.The reminders repository is correctly initialized from the client, maintaining consistency with other repository initializations in the constructor.
938-994
: LGTM! Well-structured reminder API methods.The three reminder methods (
createReminder
,updateReminder
,deleteReminder
) follow excellent patterns:
- Consistent delegation to the
remindersRepository
- Proper callback queue handling using
self.callback
- Clear method signatures with appropriate parameter types
- Good documentation explaining the nil
remindAt
behavior for "save for later" functionality- Error handling through completion blocks
The implementation is consistent with other controller methods in the codebase.
Tests/StreamChatTests/Controllers/MessageController/MessageController+Reminders_Tests.swift (4)
9-37
: LGTM! Well-structured test class setup.The test class follows excellent testing practices:
- Proper mock dependencies setup
- Clean teardown with
cleanUp()
calls- Appropriate test isolation with unique IDs
- Uses the established mocking infrastructure
41-98
: LGTM! Comprehensive create reminder tests.The test coverage for
createReminder
is thorough:
- Tests both success and failure scenarios
- Verifies repository method calls with correct parameters
- Uses proper async testing with expectations
- Validates both the result type and error propagation
- Tests both with and without
remindAt
parameter (lines 61, 86)
102-159
: LGTM! Complete update reminder test coverage.The
updateReminder
tests mirror the create tests appropriately:
- Success scenario validates correct parameter passing and result handling
- Failure scenario ensures proper error propagation
- Consistent testing pattern with the create tests
163-206
: LGTM! Thorough delete reminder test coverage.The delete reminder tests are well-implemented:
- Success test verifies nil error return and correct repository method calls
- Failure test ensures error propagation works correctly
- Uses the simpler completion signature (Error? vs Result<T, Error>) consistently with the actual API
Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController+Combine_Tests.swift (3)
11-32
: LGTM! Proper Combine test setup.The test class setup is excellent:
- Inherits from
iOS13TestCase
for Combine availability- Proper initialization with empty query
- Clean cancellables management
- Uses
AssertAsync.canBeReleased
to verify controller lifecycle
34-51
: LGTM! Comprehensive state publisher testing.The state publisher test demonstrates good testing practices:
- Uses
Record.Recording
for capturing publisher output- Tests weak reference behavior to ensure publisher keeps controller alive
- Simulates delegate callback to trigger state change
- Uses
AssertAsync.willBeEqual
for async assertion- Verifies the expected state sequence:
.localDataFetched
→.remoteDataFetched
53-81
: LGTM! Thorough reminders changes publisher testing.The reminders changes publisher test is well-implemented:
- Creates a realistic
MessageReminder
instance with proper mock data- Tests
ListChange
emission correctly- Uses the same weak reference pattern as the state test
- Verifies exact change propagation from delegate to publisher
Sources/StreamChat/WebSocketClient/Events/EventType.swift (2)
158-178
: LGTM! Well-organized reminder event types.The new reminder event types are excellently implemented:
- Proper organization with
// MARK: - Reminders
section- Consistent naming following the
messageReminder*
pattern- Includes all CRUD operations plus due notifications
- Event type values follow the established conventions
- Good documentation comments explaining each event's purpose
249-252
: LGTM! Consistent event decoding implementation.The event decoding logic properly handles all four reminder event types:
- Maps to the corresponding DTO classes (
ReminderCreatedEventDTO
, etc.)- Follows the established pattern of other event decoders
- Maintains the same error handling approach
- Properly positioned in the switch statement
DemoApp/StreamChat/Components/DemoChatMessageContentView.swift (5)
12-37
: Well-implemented UI components for "Save for Later" feature.The implementation follows good practices:
- Proper use of lazy properties for UI components
- Consistent styling using the appearance system
- Clear component hierarchy with HContainer
- Appropriate sizing constraints
50-57
: Conditional layout logic is correctly implemented.The layout logic properly:
- Checks for the
.saveForLaterInfo
option- Sets appropriate background color using the appearance system
- Adds the view to the correct container with proper constraints
- Uses consistent spacing with the pin info implementation
12-20
: Well-structured lazy view initialization with proper constraints.The
saveForLaterView
is properly implemented using a horizontal container with appropriate spacing and sizing constraints. The lazy initialization pattern is correct for UI components.
22-28
: Icon configuration looks good with proper system image usage.The bookmark icon with system name "bookmark.fill" is appropriate for the "save for later" functionality. The tint color correctly uses the accent primary color from the appearance palette.
50-57
:❓ Verification inconclusive
Verify the background color and constraint handling.
The implementation looks correct, but ensure that the
highlightedAccentBackground1
color provides sufficient contrast for accessibility and that the constraint setup doesn't conflict with existing layout constraints.Run the following script to check for potential constraint conflicts:
🏁 Script executed:
#!/bin/bash # Description: Check for other views that might use bubbleThreadFootnoteContainer to identify potential constraint conflicts rg -A 5 "bubbleThreadFootnoteContainer" --type swiftLength of output: 11640
Constraint handling verified; please confirm color contrast
- A search for other
bubbleThreadFootnoteContainer
usages shows no conflicting constraint setups.- Ensure that
appearance.colorPalette.highlightedAccentBackground1
meets WCAG contrast requirements (≥4.5:1) against any overlaid text or icons in both light and dark modes.Tests/StreamChatTests/APIClient/Endpoints/Payloads/ReminderPayloads_Tests.swift (3)
9-36
: Comprehensive test coverage for ReminderPayload.The test properly validates:
- Basic reminder properties (channelCid, messageId, dates)
- Embedded message object with all required fields
- Channel properties from the updated JSON structure
- Proper handling of nil values for optional fields
38-64
: Well-structured ReminderResponsePayload test.The test correctly:
- Uses inline JSON for clear test data visibility
- Validates the wrapper structure with duration field
- Tests all reminder properties within the response
- Follows consistent assertion patterns
66-112
: Thorough RemindersQueryPayload test coverage.The test effectively validates:
- Multiple reminders in the response array
- Pagination token handling
- Different reminder configurations (with/without remindAt)
- Array indexing and property verification
Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ReminderUpdaterMiddleware_Tests.swift (5)
9-24
: Proper test setup and teardown implementation.The test class correctly:
- Uses in-memory database for isolated testing
- Initializes middleware properly
- Follows proper teardown pattern to prevent test leakage
25-65
: Comprehensive test for reminder creation event.The test effectively:
- Sets up required channel and message data before testing
- Creates realistic event payload and DTO
- Verifies reminder is properly saved to database
- Checks reminder ID matches message ID correctly
67-120
: Thorough test for reminder update functionality.The test properly:
- Creates initial reminder state
- Updates with new reminder data
- Verifies the reminder is updated (not recreated)
- Uses
XCTAssertNearlySameDate
for appropriate date comparison
122-165
: Appropriate test for due notification event.The test correctly:
- Sets up existing reminder state
- Processes due notification event
- Verifies reminder still exists after notification (not deleted)
167-212
: Complete test for reminder deletion event.The test effectively:
- Creates reminder state
- Verifies existence before deletion
- Processes delete event
- Confirms reminder is properly removed from database
Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController+Combine.swift (3)
8-17
: Well-implemented Combine publishers for MessageReminderListController.The publishers correctly:
- Use appropriate Combine types (CurrentValueSubject for state, PassthroughSubject for changes)
- Implement proper keepAlive mechanism for lifecycle management
- Follow established patterns from other controller extensions
- Provide clean public API surface
19-34
: Proper BasePublishers implementation following established patterns.The BasePublishers class correctly:
- Initializes subjects with appropriate initial values
- Registers itself as a delegate to receive updates
- Uses multicastDelegate for proper delegation
- Follows the same pattern as other controller Combine extensions
36-47
: Correct delegate implementation forwarding events to publishers.The delegate methods properly:
- Forward state changes to the state subject
- Forward reminders changes to the remindersChanges subject
- Implement the correct delegate protocol methods
- Follow established patterns for event forwarding
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/RemindersRepository_Mock.swift (2)
10-32
: Well-structured mock implementation with comprehensive parameter tracking.The mock correctly captures all method parameters and provides preset result mechanisms for controlled testing scenarios. This enables thorough testing of repository interactions.
44-65
: Good practice: cleanUp method for test isolation.The cleanUp method properly resets all tracked values, ensuring test isolation and preventing state leakage between tests.
Tests/StreamChatTests/Controllers/MessageReminderListController/MessageReminderListController_Tests.swift (3)
29-45
: Excellent test coverage for synchronization scenarios.The test properly verifies both the repository call and the controller state changes, ensuring complete integration testing.
133-162
: Good pagination cursor testing.This test effectively verifies that the controller correctly uses pagination cursors for subsequent requests, which is crucial for proper pagination behavior.
164-232
: Comprehensive delegate integration test.This test creates a realistic scenario with database persistence and verifies the delegate callback mechanism works correctly. The use of expectations and proper setup/teardown is exemplary.
Tests/StreamChatTests/WebSocketClient/Events/ReminderEvents_Tests.swift (3)
27-36
: Thorough event decoding verification.The test properly validates all key fields from the JSON fixture, ensuring accurate parsing of reminder events.
55-62
: Good negative test case coverage.Testing the scenario where required data is missing ensures robust error handling in the domain event conversion.
28-28
:✅ Verification successful
Verify JSON fixture files exist and contain valid data.
Ensure that the JSON fixture files referenced in these tests contain realistic and up-to-date event payloads.
🏁 Script executed:
#!/bin/bash # Description: Verify that all reminder event JSON fixtures exist and are valid JSON echo "Checking for reminder event JSON fixtures..." fd -e json -p "TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder" | while read -r file; do echo "Found fixture: $file" if ! jq . "$file" > /dev/null 2>&1; then echo "ERROR: Invalid JSON in $file" exit 1 fi done echo "Verifying specific fixtures referenced in tests..." for fixture in "ReminderCreated" "ReminderUpdated" "ReminderDeleted" "ReminderDue"; do fixture_path=$(fd "${fixture}.json" TestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/ | head -1) if [ -z "$fixture_path" ]; then echo "ERROR: Missing fixture for $fixture" else echo "✓ Found fixture: $fixture_path" # Validate JSON structure contains expected fields jq -e '.message_id, .created_at, .reminder' "$fixture_path" > /dev/null 2>&1 || echo "WARNING: Missing expected fields in $fixture_path" fi doneLength of output: 4341
All reminder event JSON fixtures are present and valid
All referenced fixtures (
ReminderCreated.json
,ReminderUpdated.json
,ReminderDeleted.json
,ReminderDue.json
) were found underTestTools/StreamChatTestTools/Fixtures/JSONs/Events/Reminder/
, are valid JSON, and include the expected.message_id
,.created_at
, and.reminder
fields. No further changes needed.DemoApp/Screens/DemoAppTabBarController.swift (9)
9-9
: Good protocol conformance for reminder functionality.Adding
MessageReminderListControllerDelegate
enables proper badge updates when reminders change.
88-92
: Excellent feature flag implementation.Conditionally including the reminders tab based on configuration allows for flexible feature rollout and testing.
9-9
: Good delegation pattern implementation.The class properly conforms to
MessageReminderListControllerDelegate
to handle reminder updates.
13-15
: Properties are well-named and appropriately scoped.The new properties
reminderListVC
andallRemindersListController
follow consistent naming conventions with existing properties.
20-35
: Constructor properly handles new dependencies.The initialization correctly includes the new reminder-related parameters and assigns them to instance properties. The pattern is consistent with existing parameters.
64-68
: Good feature flag implementation for reminders.The conditional logic properly checks the feature flag before setting up reminders functionality, which is a good practice for feature rollouts.
84-92
: Tab configuration follows consistent patterns.The reminders tab setup follows the same pattern as other tabs, and the conditional inclusion based on the feature flag is well-implemented.
102-107
: Delegate method implementation is correct.The delegate method properly calls the badge update function when reminders change, maintaining UI consistency.
109-112
: Badge update logic is simple and effective.The badge shows the count when reminders exist and hides when empty, which provides clear visual feedback to users.
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (1)
135-138
: Reminderid
is set tomessageId
– verify backend contract
payload.messageId
is used for both the foreign-key relation and the reminder’s primaryid
:reminderDTO.id = payload.messageIdIf the backend delivers a dedicated
payload.id
(common for 1-N message-to-reminder relations), this will silently conflate two distinct identifiers and break deduplication & updates.
Please confirm the spec and, if needed, assignid = payload.id
.Sources/StreamChat/Controllers/MessageReminderListController/MessageReminderListController.swift (1)
168-180
: Multiple concurrentloadMoreReminders
calls can interleaveBecause
nextCursor
is updated inside the completion block, two quick successive calls will issue the same cursor and duplicate items/requests.Add a simple guard or serialize calls (e.g. store an
isLoadingMore
flag).
DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift
Outdated
Show resolved
Hide resolved
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: 4
♻️ Duplicate comments (1)
Sources/StreamChat/Repositories/RemindersRepository.swift (1)
80-108
:⚠️ Potential issueLocal database write errors are still swallowed – caller may hang indefinitely
A previous review already pointed this out.
If the optimistic local write throws, thecompletion
passed intocreateReminder
is never invoked. The UI will spin forever.- } completion: { _ in + } completion: { dbError in + if let dbError = dbError { + completion(.failure(dbError)) + return + }Apply the same pattern to
updateReminder
anddeleteReminder
.
🧹 Nitpick comments (1)
Sources/StreamChat/Repositories/RemindersRepository.swift (1)
41-55
: Consider surfacing partial-save failures to the caller
session.saveReminder
can throw.
Currently every failure is silently eaten and the reminder is omitted from the resulting array, while thecompletion
is still called with.success
.
If a reminder is missing in the UI the user has no clue something went wrong.If silent dropping is intentional please confirm. Otherwise, wrap the
compactMap
in aResult
and propagate an aggregated error (or at least log at error level).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
(2 hunks)Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift
(1 hunks)Sources/StreamChat/Repositories/RemindersRepository.swift
(1 hunks)StreamChat.xcodeproj/project.pbxproj
(64 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift
- StreamChat.xcodeproj/project.pbxproj
97e0650
to
9741b8a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (1)
88-90
:⚠️ Potential issueCritical compilation error: Invalid Core Data API usage
The method
NSEntityDescription.insertNewObject(into:for:)
does not exist in Apple's Core Data framework and will cause a compilation error.Apply this fix to use the correct Core Data API:
- let request = fetchRequest(messageId: messageId) - let new = NSEntityDescription.insertNewObject(into: context, for: request) + let new = NSEntityDescription.insertNewObject( + forEntityName: MessageReminderDTO.entityName, + into: context + ) as! MessageReminderDTOStreamChat.xcodeproj/project.pbxproj (1)
1677-1690
: Duplicate PBXBuildFile entries still presentThe duplicate entries for Message Reminder feature files identified in the previous review are still present in this code segment, which will cause build issues.
🧹 Nitpick comments (1)
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (1)
147-153
: Proper cleanup but potential optimization opportunityThe delete method correctly removes the relationship and deletes the DTO. However, it could be optimized by directly loading the reminder DTO instead of going through the message relationship.
Consider this more direct approach:
func deleteReminder(messageId: MessageId) { - let message = message(id: messageId) - guard let reminderDTO = message?.reminder else { + guard let reminderDTO = MessageReminderDTO.load(messageId: messageId, context: self) else { return } delete(reminderDTO) - message?.reminder = nil + reminderDTO.message.reminder = nil }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
DemoAppPush/NotificationService.swift
(1 hunks)Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
(2 hunks)Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift
(1 hunks)Sources/StreamChat/Repositories/RemindersRepository.swift
(1 hunks)StreamChat.xcodeproj/project.pbxproj
(64 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- Sources/StreamChat/APIClient/ChatRemoteNotificationHandler.swift
- Sources/StreamChat/Repositories/RemindersRepository.swift
🔇 Additional comments (11)
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (3)
22-29
: Efficient sorting helper implementationThe
willSave()
override efficiently maintains thesortingRemindAt
property for consistent sorting behavior whenremindAt
is nil. The conditional check prevents unnecessary property updates.
39-69
: Comprehensive query handling with proper fallbackThe
remindersFetchRequest
method properly handles dynamic sorting from the query with a sensible default fallback. The switch statement covers all expected sorting keys with graceful handling of unknown keys.
135-135
:❌ Incorrect review comment
Potential data inconsistency: ID assignment mismatch
The reminder ID is being set to
payload.messageId
instead ofpayload.id
, which could cause data inconsistency if the reminder ID and message ID are meant to be different entities.Please verify if this is intentional or if it should be:
- reminderDTO.id = payload.messageId + reminderDTO.id = payload.id
🏁 Script executed:
#!/bin/bash # Check if ReminderPayload has both id and messageId properties ast-grep --pattern 'struct ReminderPayload { $$$ }'Length of output: 55
🏁 Script executed:
#!/bin/bash # Locate the definition of ReminderPayload in the codebase rg -n "struct ReminderPayload" . # Fallback: check for class-based or typealias occurrences rg -n "class ReminderPayload" . # Inspect payload property usages to see which fields exist rg -n "payload.id" . rg -n "payload.messageId" .Length of output: 8239
🏁 Script executed:
#!/bin/bash # Show ReminderPayload class definition rg -n "class ReminderPayload" -A 20 Sources/StreamChat/APIClient/Endpoints/Payloads/ReminderPayloads.swift # Show tests for ReminderPayload rg -n "class ReminderPayload_Tests" -A 50 Tests/StreamChatTests/APIClient/Endpoints/Payloads/ReminderPayloads_Tests.swift # Show MessageReminderDTO class definition and key properties rg -n "class MessageReminderDTO" -A 20 Sources/StreamChat/Database/DTOs/MessageReminderDTO.swiftLength of output: 4386
Incorrect suggestion:
ReminderPayload
has no separateid
field
TheReminderPayload
model only includesmessageId
(noid
property), and the API’s JSON uses"message_id"
—there is no"id"
key for a reminder. AssigningreminderDTO.id = payload.messageId
is therefore intentional and correct. No change needed.Likely an incorrect or invalid review comment.
DemoAppPush/NotificationService.swift (2)
99-101
: Proper early return for reminder notificationsThe special handling for
.reminderDue
message types with early return preserves the original notification content, which is appropriate for reminder notifications that likely have pre-formatted content.
112-118
: Robust fallback handling for reminder notificationsThe fallback logic properly checks the stream payload for reminder due event types, ensuring reminder notifications are handled consistently even when they don't match the expected message notification format.
StreamChat.xcodeproj/project.pbxproj (6)
4429-4466
: Comprehensive Message Reminder feature file organizationThe file organization for the Message Reminder feature looks well-structured with proper separation of concerns:
- Controllers and their Combine extensions
- Queries and DTOs for data management
- Endpoints and payloads for API communication
- Events and middleware for real-time updates
- Repository for data persistence
- Comprehensive test coverage and mocks
- Demo UI integration
This follows good architectural patterns for the StreamChat SDK.
5606-5606
: Well-organized group structure for new featureThe Message Reminder files are properly organized into logical groups:
- Events grouped with other event types
- DTOs with other data transfer objects
- Queries with other query types
- Middleware with other event middlewares
- Payloads with other payload types
- Endpoints with other API endpoints
- Models with other domain models
- Controllers with other controller types
This maintains consistency with the existing project structure.
Also applies to: 5650-5650, 5732-5740, 5815-5815, 5837-5837, 5899-5899, 6008-6008, 6056-6056
8862-8879
: Proper controller grouping structureThe MessageReminderListController files are properly grouped with separate directories for the main implementation and tests, following the established pattern in the project.
8906-8916
: Comprehensive JSON test resources for reminder eventsGood coverage of reminder event types with dedicated JSON files:
- ReminderCreated.json
- ReminderUpdated.json
- ReminderDeleted.json
- ReminderDue.json
This ensures proper testing of all reminder event scenarios.
2496-2497
:✅ Verification successful
Verify the Filter file renaming impact
The renaming from
Filter+ChatChannel.swift
toFilter+predicate.swift
suggests the filter functionality is being extended beyond channels. Ensure this change is properly reflected throughout the codebase and doesn't break existing channel-specific filter usage.
🏁 Script executed:
#!/bin/bash # Description: Search for any remaining references to the old filename or channel-specific filter usage that might be affected # Search for old filename references rg -i "filter.*channel\.swift|filterchannel" --type swift # Search for imports or references that might need updating rg "import.*Filter.*Channel|Filter.*Channel" --type swift # Check for any channel-specific filter methods that might be affected ast-grep --pattern 'extension Filter { $$$ }'Length of output: 11727
🏁 Script executed:
#!/bin/bash # Verify that the old file reference was removed and the new one added in the Xcode project rg 'Filter\+ChatChannel.swift' -n StreamChat.xcodeproj/project.pbxproj rg 'Filter\+predicate.swift' -n StreamChat.xcodeproj/project.pbxprojLength of output: 871
Filter File Rename Verified
I confirmed there are no lingering references to
Filter+ChatChannel.swift
in the Xcode project or Swift sources—only the newFilter+predicate.swift
is listed inproject.pbxproj
. No further updates are needed.
15568-15570
:✅ Verification successful
Verify atlantis package version compatibility
The atlantis package dependency is being updated from an exact version (1.18.0) to a minimum version requirement (1.28.0) with up-to-next-major-version. This is a significant version jump that should be verified for compatibility.
🌐 Web query:
What are the breaking changes between atlantis 1.18.0 and 1.28.0? Are there any compatibility issues or new requirements?
💡 Result:
Based on the search results, I can provide information about the changes between Atlantis 1.18.0 and 1.28.0, focusing on the ProxymanApp/Atlantis repository which appears to be the most relevant to your query.
Breaking Changes and Updates in Atlantis 1.18.0 to 1.28.0
The most significant changes between versions 1.18.0 and 1.28.0 of the ProxymanApp/Atlantis tool revolve around connectivity improvements, bug fixes, and feature additions. Here's a detailed breakdown:
Major Changes
Version 1.28.0 (Released April 19, 2025): Fixed connection issues on macOS 15.4 by implementing modern networking APIs[1][6]
- Replaced deprecated NetService with NWBrowser and NWConnection
- Improved connectivity for iOS Simulators with direct connection to Proxyman App
- Maintained compatibility with iOS devices using Bonjour Service
Version 1.27.0: Updated with a new SwiftUI Sample App and revised documentation[6]
Version 1.26.0: Addressed duplicated requests issue when using custom URLProtocols[6]
Version 1.25.1 & 1.25.2: Fixed crashes when using Atlantis with AVAggregateAssetDownloadTask and updated Podspec[6]
Version 1.24.0: Added Privacy Manifest support[6]
Version 1.23.0: Added option to opt-out of Method Swizzling on WebSocket (WS/WSS) connections[6]
Version 1.22.0: Fixed crashes when intercepting URLSessionUploadTask with nil Data[6]
Compatibility Considerations
- Atlantis 1.28.0 is backward-compatible with Proxyman 5.18.0 or older versions[6]
- However, it's recommended to use Atlantis 1.28.0 with Proxyman 5.19.0 or later for optimal performance with the modern APIs[6]
WebSocket Support
The tool provides built-in support for capturing WebSocket traffic from
URLSessionWebsocketTask
but doesn't natively work with third-party WebSocket libraries such as Starscream that don't useURLSessionWebsocketTask
under the hood[1]Manual Request/Response Handling
For applications using C++ Network libraries or other non-URLSession networking stacks, Atlantis provides functionality to manually add requests and responses for inspection:
Atlantis.add(request: request, response: response, responseBody: responseData)This allows capturing network traffic from non-standard networking implementations[1]
The changes from 1.18.0 to 1.28.0 represent a series of incremental improvements rather than radical breaking changes, with the most significant update being the connectivity fixes for macOS 15.4 in the latest release.
Citations:
- 1: https://github.com/ProxymanApp/atlantis
- 2: https://github.com/runatlantis/atlantis/blob/main/CHANGELOG.md
- 3: https://www.runatlantis.io/docs/terraform-versions
- 4: https://specter2.fandom.com/wiki/Changelog/2023
- 5: https://formulae.brew.sh/formula/
- 6: https://github.com/ProxymanApp/atlantis/releases
- 7: https://www.atlantiswordprocessor.com/en/help/system_requirements.htm
- 8: https://www.scribd.com/document/551715438/Brew-Formula
- 9: https://www.cisa.gov/news-events/bulletins/sb22-213
- 10: https://www.minecraftforum.net/forums/support/server-support-and/3149244-server-crashing-you-get-the-point-im-sick-of-this
Atlantis dependency version bump is backward-compatible
We’ve verified that upgrading the Atlantis dependency from 1.18.0 to “^1.28.0” introduces only incremental improvements (macOS 15.4 connectivity fixes, bug fixes, new features) with no breaking API changes.
• Location: StreamChat.xcodeproj/project.pbxproj (lines 15568–15570)
• No removed or replaced APIs between 1.18.0 and 1.28.0
• Atlantis 1.28.0 remains compatible with Proxyman 5.18.0+, though Proxyman 5.19.0+ is recommended for optimal performanceNo further action required.
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
♻️ Duplicate comments (3)
DemoApp/Screens/DemoReminderListVC.swift (3)
186-208
: Adddeinit
to stop the timer and avoid a retain-cycle leak
refreshTimer
holds a strong reference to itstarget
(self
).
If the VC is popped while the timer is still scheduled, both will leak.
A simpledeinit { stopRefreshTimer() }
will guarantee cleanup.
381-384
: Handle theError
returned bysynchronize
The closure parameter is an
Error?
, but it is ignored.
Surface the error to the user (alert / banner) or at least log it so failures are discoverable during QA.
491-505
:activeController
is recreated but never synchronisedAfter swapping in a fresh controller you immediately read
activeController.reminders
.
Without asynchronize()
call the list will stay empty until the user manually refreshes.
CallactiveController.synchronize()
right after setting the delegate.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
DemoApp/Screens/DemoReminderListVC.swift
(1 hunks)DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift
(3 hunks)DemoAppPush/NotificationService.swift
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- DemoAppPush/NotificationService.swift
- DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift
🔇 Additional comments (1)
DemoApp/Screens/DemoReminderListVC.swift (1)
460-465
: Race condition when switching filters during pagination
reminders = Array(activeController.reminders)
assumes thatactiveController
did not change during an on-going pagination request (seeisPaginatingReminders
).
If the user changes the filter while a page is loading,loadMoreReminders
’s completion will mutateisPaginatingReminders
of the old controller, potentially leaving the new one blocked.Consider scoping
isPaginatingReminders
per-controller or cancelling outstanding loads when switching filters.
# Conflicts: # StreamChat.xcodeproj/project.pbxproj
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 (1)
CHANGELOG.md (1)
12-15
:⚠️ Fix list indentation
Nested sub-bullets should be indented with 2 spaces to comply with markdown style guidelines.Proposed diff:
- - Add `ChatMessageController.createReminder()` - - Add `ChatMessageController.updateReminder()` - - Add `ChatMessageController.deleteReminder()` - - Add `MessageReminderListController` and `MessageReminderListQuery` + - Add `ChatMessageController.createReminder()` + - Add `ChatMessageController.updateReminder()` + - Add `ChatMessageController.deleteReminder()` + - Add `MessageReminderListController` and `MessageReminderListQuery`🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
12-12: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
CHANGELOG.md
(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
(5 hunks)Sources/StreamChat/Controllers/MessageController/MessageController.swift
(3 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift
(3 hunks)Sources/StreamChat/Database/DatabaseSession.swift
(2 hunks)StreamChat.xcodeproj/project.pbxproj
(64 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
- Sources/StreamChat/Database/DatabaseSession.swift
- Sources/StreamChat/Controllers/MessageController/MessageController.swift
- StreamChat.xcodeproj/project.pbxproj
- Sources/StreamChat/Database/DTOs/MessageDTO.swift
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
12-12: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Test LLC (Debug)
- GitHub Check: Automated Code Review
- GitHub Check: Metrics
🔇 Additional comments (3)
CHANGELOG.md (3)
10-10
: ✅ Correct: Document Filter.isNil addition
The newFilter.isNil
API is accurately reflected in the changelog under the added section.
11-11
: ✅ Document Message Reminders
The top-levelAdd Message Reminders
entry correctly surfaces the new feature for users.
1-15
: ❓ Document file rename
The PR also renamesFilter+ChatChannel.swift
toFilter+predicate
. Consider adding a “Renamed” or “Changed” entry in the changelog so consumers are aware of the updated filename and import paths.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
12-12: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
8bb1b97
to
3f59a94
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (1)
282-305
: Consider adding the messageRemindersEnabled flag for consistency.The
dummyPayloadWithNoExtraData
function creates aChannelConfig
inline but doesn't include themessageRemindersEnabled
flag, which could lead to inconsistent test behavior depending on which dummy data function is used.For consistency, consider adding the flag here as well:
config: .init( reactionsEnabled: true, typingEventsEnabled: true, readEventsEnabled: true, connectEventsEnabled: true, uploadsEnabled: true, repliesEnabled: true, quotesEnabled: true, searchEnabled: true, mutesEnabled: true, urlEnrichmentEnabled: true, + messageRemindersEnabled: true, messageRetention: "1000", maxMessageLength: 100,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift
(1 hunks)Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
(1 hunks)Tests/StreamChatTests/WebSocketClient/Events/ReminderEvents_Tests.swift
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
- Tests/StreamChatTests/WebSocketClient/Events/ReminderEvents_Tests.swift
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Metrics
🔇 Additional comments (1)
TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (1)
142-142
: LGTM! Test data correctly enables message reminders feature.The addition of
messageRemindersEnabled: true
to the default channel configuration ensures that test scenarios properly reflect the new message reminders capability.
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)
CHANGELOG.md (2)
6-9
: Document theFilter+predicate
file rename.The PR renames
Filter+ChatChannel.swift
toFilter+predicate.swift
to generalize filter extensions beyond channels, but the changelog only lists the addition ofFilter.isNil
. Please add a "🔄 Changed" entry under the Upcoming## StreamChat
section to reflect this rename. For example:### 🔄 Changed - Renamed `Filter+ChatChannel.swift` → `Filter+predicate.swift` to extend filter helpers beyond channels
10-13
: Fix markdown list indentation for nested items.The nested bullets under
- Add Message Reminders [#3623]
are indented by 3 spaces, but markdownlint expects 2 spaces for nested list items.
Apply this diff:- - Add `ChatMessageController.createReminder()` - - Add `ChatMessageController.updateReminder()` - - Add `ChatMessageController.deleteReminder()` - - Add `MessageReminderListController` and `MessageReminderListQuery` + - Add `ChatMessageController.createReminder()` + - Add `ChatMessageController.updateReminder()` + - Add `ChatMessageController.deleteReminder()` + - Add `MessageReminderListController` and `MessageReminderListQuery`🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
10-10: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
CHANGELOG.md
(1 hunks)DemoAppPush/NotificationService.swift
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- DemoAppPush/NotificationService.swift
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
10-10: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
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)
CHANGELOG.md (2)
8-9
: Document file rename as part of PR. The PR also renamesFilter+ChatChannel.swift
toFilter+predicate
to extend its reuse beyond channels. Please add a bullet under this section to capture that breaking/user-facing change.
10-13
: Fix sub-list indentation. The nested bullets under "Add Message Reminders" use 3 spaces but should follow the project's 2-space indentation per list level (MD007). For example:- - Add `ChatMessageController.createReminder()` + - Add `ChatMessageController.createReminder()`🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
10-10: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3(MD007, ul-indent)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
CHANGELOG.md
(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
10-10: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 2; Actual: 3
(MD007, ul-indent)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Automated Code Review
- GitHub Check: Test LLC (Debug)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Metrics
🔇 Additional comments (1)
CHANGELOG.md (1)
7-7
: Changelog section header formatting is consistent. Using### ✅ Added
aligns with the style of other release entries and clearly denotes the added features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
DemoApp/Screens/DemoReminderListVC.swift (4)
186-208
: Timer leaks if the VC is deallocated while hiddenThis issue was previously identified. Add cleanup in
deinit
for safety:deinit { stopRefreshTimer() }
381-384
: Surface synchronisation errors to the userThis issue was previously identified.
controller.synchronize { _ in }
ignores theError?
. Handle the callback and show an alert or retry banner if the network call fails.
386-395
: Propagate pagination errors to the UIThis issue was previously identified.
loadMoreReminders
swallows theError?
returned byloadMoreReminders { … }
. Handle the error and inform the user of pagination failures.
491-505
: 🛠️ Refactor suggestionControllers recreated but not synchronised and incomplete filter handling
This partially duplicates a previous issue - controllers are recreated but
synchronize()
is not called. Additionally, the logic only handlesupcoming
,overdue
, andscheduled
filters but ignoresall
andlater
filters when updating with new date.private func updateReminderListsWithNewNowDate() { + allRemindersController = FilterOption.all.makeController(client: currentUserController.client) upcomingRemindersController = FilterOption.upcoming.makeController(client: currentUserController.client) overdueRemindersController = FilterOption.overdue.makeController(client: currentUserController.client) scheduledRemindersController = FilterOption.scheduled.makeController(client: currentUserController.client) - if selectedFilter == .upcoming { + laterRemindersController = FilterOption.later.makeController(client: currentUserController.client) + + switch selectedFilter { + case .all: + activeController = allRemindersController + case .upcoming: activeController = upcomingRemindersController - } else if selectedFilter == .overdue { + case .overdue: activeController = overdueRemindersController - } else if selectedFilter == .scheduled { + case .scheduled: activeController = scheduledRemindersController - } else { - return + case .later: + activeController = laterRemindersController } + activeController.delegate = self + activeController.synchronize() updateRemindersData() }
🧹 Nitpick comments (2)
DemoApp/Screens/DemoReminderListVC.swift (2)
409-411
: Fix suspicious calculation for 1-minute reminder.The calculation
1.05 * 60
for a 1-minute reminder seems incorrect. This results in 63 seconds instead of 60 seconds. If the intention is to add a small buffer, this should be documented or use a more explicit approach.- alert.addAction(UIAlertAction(title: "Remind in 1 Minutes", style: .default) { [weak self] _ in - let date = Date().addingTimeInterval(1.05 * 60) + alert.addAction(UIAlertAction(title: "Remind in 1 Minute", style: .default) { [weak self] _ in + let date = Date().addingTimeInterval(60) // 1 minute self?.updateReminderDate(for: reminder, newDate: date) })
703-762
: Consider simplifying time formatting logic.The time formatting logic works correctly but has duplicate rounding calculations. Consider extracting the common rounding logic to reduce code duplication.
+ private func roundTimeInterval(_ timeInterval: TimeInterval) -> TimeInterval { + let roundedMinutes = ceil(timeInterval / 60 - 0.5) + return max(roundedMinutes * 60, 60) // Minimum 1 minute + } + private func formatOverdueTime(timeInterval: TimeInterval) -> String { - // Round to the nearest minute (30 seconds or more rounds up) - let roundedMinutes = ceil(timeInterval / 60 - 0.5) - let roundedInterval = roundedMinutes * 60 - - // If less than a minute, show "1 min" instead of "0 min" - if roundedInterval == 0 { - return "Overdue by 1 min" - } + let roundedInterval = roundTimeInterval(timeInterval) let formatter = DateComponentsFormatter() // ... rest of the formatting logic } private func formatDueTime(timeInterval: TimeInterval) -> String { - // Round to the nearest minute (30 seconds or more rounds up) - let roundedMinutes = ceil(timeInterval / 60 - 0.5) - let roundedInterval = roundedMinutes * 60 - - // If less than a minute, show "1 min" instead of "0 min" - if roundedInterval == 0 { - return "1m" - } + let roundedInterval = roundTimeInterval(timeInterval) + + if roundedInterval == 60 { + return "1m" + } let formatter = DateComponentsFormatter() // ... rest of the formatting logic }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
DemoApp/Screens/DemoReminderListVC.swift
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Test LLC (Debug)
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Automated Code Review
- GitHub Check: Metrics
🔇 Additional comments (3)
DemoApp/Screens/DemoReminderListVC.swift (3)
398-459
: Well-implemented user interaction handlers.The error handling, user avatar interaction, and edit reminder options are well-designed and provide good user experience. The alert presentation with proper popover configuration for iPad is a nice touch.
510-581
: Solid table view implementation with good UX.The table view implementation includes proper error handling for deletion, well-configured swipe actions, and appropriate navigation to channels with pagination parameters. The scroll-based pagination trigger is also well-implemented.
9-765
: Well-architected reminder list implementation.This is a comprehensive and well-structured implementation of the reminder list feature. The architecture demonstrates good separation of concerns, proper use of delegation patterns, and follows iOS best practices. The filter system is particularly well-designed with dedicated controllers for performance optimization.
|
🔗 Issue Links
IOS-696
🎯 Goal
Add message reminders feature. This will allow users to save messages for later and get notified about them.
📝 Summary
Docs PR: https://github.com/GetStream/docs-content/pull/260
New APIs:
Filter.isNil
to make it easier to filter nil valuesChatMessageController.createReminder()
ChatMessageController.updateReminder()
ChatMessageController.deleteReminder()
MessageReminderListController
Demo App new features:
Bonus:
Filter+ChatChannel.swift
toFilter+predicate
so that we can start using it for other data and not only channels.🎨 Showcase
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-03-28.at.16.01.16.mp4
🧪 Manual Testing Notes
In order to test the feature, you need to change the API Key to Frankfurt C2 Staging:
Saving a message as a reminder in the message list:
Saving a message for later in the message list:
Removing a reminder in the message list:
Updating a reminder in the message list:
Updating a saved for later message in the message list:
Fetching and filtering all reminders:
Deleting a message with a reminder:
Reminder when is overdue should be removed from upcoming:
Reminder Due Push Notification:
☑️ Contributor Checklist
docs-content
repoSummary by CodeRabbit
Summary by CodeRabbit