Skip to content

Conversation

nuno-vieira
Copy link
Member

@nuno-vieira nuno-vieira commented Sep 26, 2025

🔗 Issue Links

https://linear.app/stream/issue/IOS-855/ios-support-push-preferences

🎯 Goal

Add support for push preferences to control the push notifications per channel and globally.

📝 Summary

  • Adds CurrentChatUserController.setPushPreference(level:) - Sets the push preferences globally for the current user.
  • Adds CurrentChatUserController.snoozePushNotifications(until:) - Snoozes pushes globally for the current user.
  • Adds ChatChannelController.setPushPreference(level:) - Sets the push preferences of a specific channel.
  • Adds ChatChannelController.snoozePushNotifications(until:) - Snoozes pushes of a specific channel.

🛠 Implementation

setPushPreferences(level:): This function accepts a level, which can be none, mentions or all:

  • all: All messages will trigger a notification.
  • mentions: Only mentions will trigger a notification.
  • none: Will disable push notifications permanently until the level is changed again.

snoozePushNotifications(until:): This function accepts a date until when the notifications should be snoozed. Once it reaches this date, the notifications will start working again. If setPushPreference(level:) is called, it will remove the snooze.

🎨 Showcase

Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-09-26.at.12.14.06.mp4

🧪 Manual Testing Notes

Global Push Prefs:

  1. Tap on top left avatar icon
  2. Tap on "Show Profile"
  3. Tap on "Push Pref Settings"
  4. Test all settings and make sure the push notifications respect the preferences

Channel Push Prefs:

  1. Go to Channel List
  2. Swipe a channel and tap on "..."
  3. Tap on "Channel Push Pref Settings" (Last in the list)
  4. Test all settings and make sure the push notifications respect the preferences

☑️ Contributor Checklist

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

Summary by CodeRabbit

  • New Features

    • Manage push notification preferences (All, Mentions, None) globally and per-channel, including temporary snooze until a chosen date/time.
    • New Push Preferences UI with Save / Disable actions, loading state, and success/error feedback; accessible from User Profile and channel “More” menu.
    • User profile screen updated to edit profile and push settings.
  • Chores

    • Minimum iOS deployment target raised to 15.6.
  • Tests

    • Added coverage for push preference API, persistence, controllers, and payloads.

@nuno-vieira nuno-vieira requested a review from a team as a code owner September 26, 2025 11:08
Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds end-to-end push notification preferences: new API endpoint/payloads, model types, Core Data entity and mappings, updater/controller methods, demo UI (SwiftUI + UIKit hosting), router integration, test coverage, fixtures, mocks, and project file updates.

Changes

Cohort / File(s) Summary
UI: Demo screens & router
DemoApp/Screens/UserProfile/PushPreferencesView.swift, DemoApp/Screens/UserProfile/UnreadDetailsView.swift, DemoApp/Screens/UserProfile/UserProfileViewController.swift, DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift
Adds SwiftUI PushPreferencesView, introduces UserProfileViewController, removes old UIKit controller in UnreadDetailsView.swift, and wires presentation of PushPreferencesView from profile VC and channel "More" router action using UIHostingController.
API: Endpoints & paths
Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift, .../EndpointPath+OfflineRequest.swift, .../UserEndpoints.swift
Adds EndpointPath.pushPreferences, marks it as online-only, and adds POST endpoint Endpoint.pushPreferences(_:).
API: Payloads & mapping
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift, .../ChannelListPayload.swift, .../CurrentUserPayloads.swift, .../UserPayloads.swift
New request/response push preference payloads, coding keys, decoding/encoding, and conversion helpers for user/channel preference mappings.
Controllers: Public APIs
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift, Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
Adds setPushPreference(level:completion:) and snoozePushNotifications(until:completion:) to channel and current-user controllers.
Workers: Network & updater flows
Sources/StreamChat/Workers/ChannelUpdater.swift, Sources/StreamChat/Workers/CurrentUserUpdater.swift
Implements updater methods calling POST /push_preferences, parses responses, persists PushPreference payloads to DB, and returns resulting models or errors.
Database: DTOs, session & model schema
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift, .../ChannelDTO.swift, .../CurrentUserDTO.swift, Sources/StreamChat/Database/DatabaseSession.swift, .../StreamChatModel.xcdatamodeld/.../contents
Adds PushPreferenceDTO Core Data entity, relationships to ChannelDTO/CurrentUserDTO, savePushPreference DB API, and model schema updates.
Models & mapping
Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift, .../PushPreference.swift, Sources/StreamChat/Models/Channel.swift, .../CurrentUser.swift, Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift
Adds PushPreference and PushPreferenceLevel; attaches optional pushPreference to ChatChannel and CurrentChatUser; updates payload→model mapping and equality.
Updaters & Updater Mocks
TestTools/.../Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift, TestTools/.../Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
Extends mocks to record/expose setPushPreference calls and completion results; adds tracking fields and cleanup resets.
Tests, fixtures & test helpers
Tests/..., TestTools/StreamChatTestTools/Fixtures/JSONs/*.json, TestTools/.../DummyData/*.swift, TestTools/.../Mocks/*
Adds/updates unit tests, fixtures, and test helpers to include push preference payloads and validate encoding/decoding, mapping, endpoints, updaters, controllers, DB persistence; updates mocks and fixtures.
Project & build
StreamChat.xcodeproj/project.pbxproj
Adds new source files/groups for PushPreferences and UserProfile, updates file references/build entries and tests, and raises iOS deployment target.
Test mocks: DatabaseSession
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
Adds savePushPreference(id:payload:) delegation and adjusts several mock wrappers to call underlying session without returning results.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant R as DemoChatChannelListRouter
  participant V as PushPreferencesView (SwiftUI)
  participant CC as ChatChannelController
  participant W as ChannelUpdater
  participant API as REST API
  participant DB as Core Data

  U->>R: Tap "More" → "Set Channel Push Preferences"
  R->>V: Present UIHostingController (initialPreference & callbacks)
  V->>CC: onSetPreferences(level) / onDisableNotifications(date)
  CC->>W: setPushPreference(...) / snoozePushNotifications(...)
  W->>API: POST /push_preferences
  API-->>W: PushPreferencesPayloadResponse
  W->>DB: Persist PushPreferenceDTO
  W-->>CC: Result<PushPreference, Error>
  CC-->>V: Completion (success/error) -> UI update / dismiss
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant P as UserProfileViewController
  participant V as PushPreferencesView (SwiftUI)
  participant CUC as CurrentChatUserController
  participant Wu as CurrentUserUpdater
  participant API as REST API
  participant DB as Core Data

  U->>P: Open profile → Push Preferences
  P->>V: Present UIHostingController
  V->>CUC: setPushPreference(level) / snooze(until)
  CUC->>Wu: setPushPreference(payload)
  Wu->>API: POST /push_preferences
  API-->>Wu: Response (userPreferences)
  Wu->>DB: Save CurrentUser PushPreferenceDTO
  Wu-->>CUC: Result<PushPreference, Error>
  CUC-->>V: Completion -> show success / dismiss
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

🌐 SDK: StreamChat (LLC), ✅ Feature

Suggested reviewers

  • martinmitrevski
  • laevandus

Poem

I nibble code and hop through prefs tonight,
I tuck a snooze where notifications light.
Mentions, none, or all — a carrot-led decree,
I save, I snooze, I hop — now quiet, let me be. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This PR introduces several unrelated changes beyond push preference support—such as the new UserProfileViewController feature, removal of a UIKit controller in UnreadDetailsView, broad refactors in DatabaseSession_Mock, and deployment target bumps—which are outside the scope of IOS-855. Please isolate push preference work into its own focused changeset and relocate the unrelated UI enhancements, mock refactoring, and project configuration updates into separate cleanup or feature PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely summarizes the primary change by indicating the addition of push preferences support, matching the core purpose of the pull request without extraneous detail or ambiguity.
Linked Issues Check ✅ Passed The pull request fully implements the features outlined in IOS-855 by adding global and per-channel setPushPreference and snoozePushNotifications methods to both CurrentChatUserController and ChatChannelController, extending the API endpoint and payloads for push preferences, updating database models and DTOs, and providing UI and comprehensive tests for all new behaviors.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add/push-pref

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

❤️ Share

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

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamChat 8.13 MB 8.25 MB +123 KB 🟢
StreamChatUI 4.88 MB 4.89 MB +16 KB 🟢

@Stream-SDK-Bot
Copy link
Collaborator

SDK Performance

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 5.01 ms 49.9% 🔼 🟢
Duration 2.6 s 2.55 s 1.92% 🔼 🟢
Hitch time ratio 4 ms per s 1.97 ms per s 50.75% 🔼 🟢
Frame rate 75 fps 77.88 fps 3.84% 🔼 🟢
Number of hitches 1 0.4 60.0% 🔼 🟢

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (24)
Sources/StreamChat/Models/PushPreferences/PushPreference.swift (1)

7-13: Public model addition: LGTM; consider Equatable/Sendable.

Optional but useful for comparisons and concurrency.

-public struct PushPreference {
+public struct PushPreference: Equatable, Sendable {
DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (1)

710-731: Present PushPreferencesView inside a UINavigationController so the title shows.

Presented bare UIHostingController won’t display the navigation title.

-                let hostingController = UIHostingController(rootView: pushPreferencesView)
-                hostingController.title = "Channel Push Preferences - \(cid.id)"
-
-                rootViewController.present(hostingController, animated: true)
+                let hostingController = UIHostingController(rootView: pushPreferencesView)
+                hostingController.title = "Channel Push Preferences - \(cid.id)"
+                let nav = UINavigationController(rootViewController: hostingController)
+                rootViewController.present(nav, animated: true)
Sources/StreamChat/Models/Channel.swift (1)

256-256: Assignment OK, but ensure downstream init sites pass it.

The stored property is set, but without forwarding in replacing/changing it may get lost (see prior comment).

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

722-748: Deduplicate push-preference write logic.

CurrentUserUpdater and ChannelUpdater duplicate the same request/save flow. Extract a small helper to reduce drift.

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

1702-1708: Guard channel creation state as well (consistency).

Most mutations guard both cid and isChannelAlreadyCreated. Do the same here to avoid calls before backend creation.

Apply this diff:

-        guard let channelId = cid else {
+        guard let channelId = cid, isChannelAlreadyCreated else {
             callback {
                 completion?(.failure(ClientError.ChannelNotCreatedYet()))
             }
             return
         }

1731-1736: Same here: check isChannelAlreadyCreated.

Align snooze behavior with other mutations.

Apply this diff:

-        guard let channelId = cid else {
+        guard let channelId = cid, isChannelAlreadyCreated else {
             callback {
                 completion?(.failure(ClientError.ChannelNotCreatedYet()))
             }
             return
         }

1694-1721: Optional: add async variants for new APIs.

Consider async throws wrappers for parity with other controller APIs.

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

221-223: Use actual user ID when saving current user’s push preferences
Replace the hard-coded "currentUserId" with the current user’s real ID to match how user preferences are indexed elsewhere.

Sources/StreamChat/Workers/CurrentUserUpdater.swift:221

-   try $0.savePushPreference(
-       id: "currentUserId",
+   try $0.savePushPreference(
+       id: $0.currentUser?.user.id ?? "currentUserId",
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (2)

7-19: Prefer precise naming: channelCid instead of channelId.

The request maps to "channel_cid". Naming the property channelCid avoids ambiguity with a plain channel id.

Apply this diff and update call sites:

 struct PushPreferenceRequestPayload: Encodable {
     let chatLevel: String?
-    let channelId: String?
+    let channelCid: String?
     let disabledUntil: Date?
     let removeDisable: Bool?

     enum CodingKeys: String, CodingKey {
         case chatLevel = "chat_level"
-        case channelId = "channel_cid"
+        case channelCid = "channel_cid"
         case disabledUntil = "disabled_until"
         case removeDisable = "remove_disable"
     }
 }

7-11: Avoid “stringly-typed” chat level in requests.

Use PushPreferenceLevel instead of String for chatLevel to prevent typos and align with the model. This pairs with making PushPreferenceLevel Codable (see separate comment).

If you make PushPreferenceLevel Codable, change:

-    let chatLevel: String?
+    let chatLevel: PushPreferenceLevel?
Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift (2)

98-104: Disambiguate persistence key to avoid id-space collisions.

Using the bare user id as PushPreferenceDTO.id can collide with channel cids if shared space is ever mixed. Prefix the id (e.g., "user:") for clarity and safety; mirror with "channel:" on channel side.

- dto.pushPreference = try savePushPreference(id: payload.id, payload: pushPreference)
+ dto.pushPreference = try savePushPreference(id: "user:\(payload.id)", payload: pushPreference)

211-223: Prefetch pushPreference to avoid faults when materializing CurrentChatUser.

Include the new relationship in prefetchedRelationshipKeyPaths.

Apply this diff:

 extension CurrentUserDTO {
     override class func prefetchedRelationshipKeyPaths() -> [String] {
         [
             KeyPath.string(\CurrentUserDTO.channelMutes),
             KeyPath.string(\CurrentUserDTO.currentDevice),
             KeyPath.string(\CurrentUserDTO.devices),
             KeyPath.string(\CurrentUserDTO.flaggedMessages),
             KeyPath.string(\CurrentUserDTO.flaggedUsers),
             KeyPath.string(\CurrentUserDTO.mutedUsers),
-            KeyPath.string(\CurrentUserDTO.user)
+            KeyPath.string(\CurrentUserDTO.user),
+            KeyPath.string(\CurrentUserDTO.pushPreference)
         ]
     }
 }
Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift (2)

8-9: Adopt Codable and Sendable.

This value type is a perfect candidate for Codable and Sendable, simplifying payloads and improving concurrency safety.

-public struct PushPreferenceLevel: RawRepresentable, Equatable, ExpressibleByStringLiteral {
+public struct PushPreferenceLevel: RawRepresentable, Codable, Equatable, Sendable, ExpressibleByStringLiteral {

19-25: Make well-known levels constants (let), not vars.

These should be immutable public constants.

-    public static var none: PushPreferenceLevel = "none"
+    public static let none: PushPreferenceLevel = "none"
     /// Push notifications will only be delivered for mentions.
-    public static var mentions: PushPreferenceLevel = "mentions"
+    public static let mentions: PushPreferenceLevel = "mentions"
     /// All push notifications will be delivered.
-    public static var all: PushPreferenceLevel = "all"
+    public static let all: PushPreferenceLevel = "all"
DemoApp/Screens/UserProfile/PushPreferencesView.swift (4)

56-81: Use Button instead of onTapGesture for better accessibility.

Buttons provide proper accessibility semantics, focus handling, and larger hit targets.

-                    ForEach([PushPreferenceLevel.all, .mentions, .none], id: \.rawValue) { level in
-                        HStack {
+                    ForEach([PushPreferenceLevel.all, .mentions, .none], id: \.rawValue) { level in
+                        Button(action: {
+                            if disableUntil == nil { selectedLevel = level }
+                        }) {
+                            HStack {
                             VStack(alignment: .leading, spacing: 4) {
                                 Text(levelTitle(for: level))
                                     .font(.headline)
                                 Text(levelDescription(for: level))
                                     .font(.caption)
                                     .foregroundColor(.secondary)
                             }
 
                             Spacer()
 
                             if selectedLevel == level {
                                 Image(systemName: "checkmark.circle.fill")
                                     .foregroundColor(.blue)
                             }
-                        }
-                        .contentShape(Rectangle())
-                        .onTapGesture {
-                            if disableUntil == nil {
-                                selectedLevel = level
-                            }
-                        }
-                        .disabled(disableUntil != nil)
-                        .opacity(disableUntil != nil ? 0.5 : 1.0)
+                            }
+                        }
+                        .buttonStyle(.plain)
+                        .disabled(disableUntil != nil)
+                        .opacity(disableUntil != nil ? 0.5 : 1.0)
                     }

156-163: Prefer .toolbar over deprecated navigationBarItems on iOS 15+.

Modern toolbar APIs integrate better with SwiftUI navigation.

-            .navigationBarItems(
-                leading: Button("Cancel") {
-                    onDismiss()
-                }
-                .disabled(isLoading)
-            )
+            .toolbar {
+                ToolbarItem(placement: .cancellationAction) {
+                    Button("Cancel") { onDismiss() }
+                        .disabled(isLoading)
+                }
+            }

109-142: Consider system-tinted buttons for contrast/dark mode.

Hard-coded listRowBackground colors can have poor contrast. Prefer .tint on Button and default list row background.

No code change required now.


22-27: Minor: Make the DateFormatter static to avoid reallocation.

A static formatter avoids repeated instantiation during view lifecycle.

-    private let dateFormatter: DateFormatter = {
+    private static let dateFormatter: DateFormatter = {
         let formatter = DateFormatter()
         formatter.dateStyle = .medium
         formatter.timeStyle = .short
         return formatter
     }()

Then call as Self.dateFormatter.

Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (2)

24-27: Fix copy/paste comment: channel update, not currentUser.

Clarify the comment to avoid confusion.

-        // Trigger currentUser update whenever push preference is updated.
+        // Trigger channel update whenever push preference is updated.
         if let channel = self.channel, hasPersistentChangedValues, !channel.hasChanges {
             channel.id = channel.id
         }

63-72: Consider prefixing ids to disambiguate entity space.

To avoid accidental collisions between user and channel push preferences, prefix ids (e.g., "user:", "channel:") at creation time.

No code change required here; align callers.

DemoApp/Screens/UserProfile/UserProfileViewController.swift (2)

48-52: Fix ambiguous header/footer constraints and avoid force unwraps.

  • imageView lacks a vertical constraint (ambiguous layout).
  • Constraints reference view.centerXAnchor while the views live inside tableHeader/FooterView; prefer constraining to their superviews.
  • Avoid superview! to satisfy SwiftLint and zero‑warnings policy.

As per coding guidelines

Apply this diff to fix constraints within this block (adds missing centerY and anchors to the proper containers):

 NSLayoutConstraint.activate([
   imageView.widthAnchor.constraint(equalToConstant: 60),
   imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
-  imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
-  updateButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+  imageView.centerXAnchor.constraint(equalTo: tableView.tableHeaderView!.centerXAnchor),
+  imageView.centerYAnchor.constraint(equalTo: tableView.tableHeaderView!.centerYAnchor),
+  updateButton.centerXAnchor.constraint(equalTo: tableView.tableFooterView!.centerXAnchor),
   updateButton.heightAnchor.constraint(equalToConstant: 35),
-  updateButton.centerYAnchor.constraint(equalTo: updateButton.superview!.centerYAnchor)
+  updateButton.centerYAnchor.constraint(equalTo: tableView.tableFooterView!.centerYAnchor)
 ])

Optionally, make it safer and silence force‑unwrap warnings:

guard let header = tableView.tableHeaderView, let footer = tableView.tableFooterView else { assertionFailure("Missing header/footer"); return }
NSLayoutConstraint.activate([
  imageView.centerXAnchor.constraint(equalTo: header.centerXAnchor),
  imageView.centerYAnchor.constraint(equalTo: header.centerYAnchor),
  updateButton.centerXAnchor.constraint(equalTo: footer.centerXAnchor),
  updateButton.centerYAnchor.constraint(equalTo: footer.centerYAnchor),
])

Also applies to: 63-70


137-145: Don’t use synchronous Data(contentsOf:) for remote images.

Blocks a thread and bypasses caching; also lacks cancellation. Use URLSession + caching (or the project’s image loader) on a background queue, then update on main.

As per coding guidelines

Example:

let url = imageURL
let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
    guard let data, let image = UIImage(data: data) else { return }
    DispatchQueue.main.async { self?.imageView.image = image }
}
task.resume()

If you already have an image pipeline in StreamChat/StreamChatUI, prefer that for consistency.

Sources/StreamChat/Database/DTOs/ChannelDTO.swift (1)

179-190: Prefetch pushPreference to reduce Core Data faults.

Include the new relationship in prefetched key paths to avoid extra fetches during list rendering.

Apply this diff:

         [
             KeyPath.string(\ChannelDTO.currentlyTypingUsers),
             KeyPath.string(\ChannelDTO.pinnedMessages),
             KeyPath.string(\ChannelDTO.messages),
             KeyPath.string(\ChannelDTO.members),
             KeyPath.string(\ChannelDTO.reads),
-            KeyPath.string(\ChannelDTO.watchers)
+            KeyPath.string(\ChannelDTO.watchers),
+            KeyPath.string(\ChannelDTO.pushPreference)
         ]
Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (1)

2-2: Model saved with older tools version.

lastSavedToolsVersion decreased (23605 -> 23507). Consider re‑saving with the current Xcode to avoid potential warnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d3724f9 and 9f5594e.

📒 Files selected for processing (26)
  • DemoApp/Screens/UserProfile/PushPreferencesView.swift (1 hunks)
  • DemoApp/Screens/UserProfile/UnreadDetailsView.swift (9 hunks)
  • DemoApp/Screens/UserProfile/UserProfileViewController.swift (1 hunks)
  • DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (2 hunks)
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (2 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift (3 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift (3 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/UserPayloads.swift (1 hunks)
  • Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1 hunks)
  • Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1 hunks)
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1 hunks)
  • Sources/StreamChat/Database/DTOs/ChannelDTO.swift (4 hunks)
  • Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift (4 hunks)
  • Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1 hunks)
  • Sources/StreamChat/Database/DatabaseSession.swift (1 hunks)
  • Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (4 hunks)
  • Sources/StreamChat/Models/Channel.swift (3 hunks)
  • Sources/StreamChat/Models/CurrentUser.swift (3 hunks)
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (1 hunks)
  • Sources/StreamChat/Models/PushPreferences/PushPreference.swift (1 hunks)
  • Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift (1 hunks)
  • Sources/StreamChat/Workers/ChannelUpdater.swift (1 hunks)
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift (1 hunks)
  • StreamChat.xcodeproj/project.pbxproj (19 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/UserPayloads.swift
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift
  • Sources/StreamChat/Workers/ChannelUpdater.swift
  • Sources/StreamChat/Database/DatabaseSession.swift
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift
  • Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift
  • DemoApp/Screens/UserProfile/UserProfileViewController.swift
  • Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift
  • DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift
  • Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
  • Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift
  • Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift
  • Sources/StreamChat/Models/PushPreferences/PushPreference.swift
  • Sources/StreamChat/Models/Channel.swift
  • Sources/StreamChat/Database/DTOs/ChannelDTO.swift
  • DemoApp/Screens/UserProfile/UnreadDetailsView.swift
  • DemoApp/Screens/UserProfile/PushPreferencesView.swift
  • Sources/StreamChat/Models/CurrentUser.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

  • Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/UserPayloads.swift
  • Sources/StreamChat/Workers/CurrentUserUpdater.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift
  • Sources/StreamChat/Workers/ChannelUpdater.swift
  • Sources/StreamChat/Database/DatabaseSession.swift
  • Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
  • Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
  • Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift
  • Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift
  • Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift
  • Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
  • Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift
  • Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift
  • Sources/StreamChat/Models/PushPreferences/PushPreference.swift
  • Sources/StreamChat/Models/Channel.swift
  • Sources/StreamChat/Database/DTOs/ChannelDTO.swift
  • Sources/StreamChat/Models/CurrentUser.swift
🧠 Learnings (2)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

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

Applied to files:

  • StreamChat.xcodeproj/project.pbxproj
🧬 Code graph analysis (17)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1)
  • asModel (33-35)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (7)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • setPushPreference (1698-1721)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • setPushPreference (464-480)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (3)
  • asModel (30-35)
  • asModel (58-60)
  • asModel (64-72)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (2)
  • asModel (33-35)
  • savePushPreference (53-59)
Sources/StreamChat/Database/DatabaseContainer.swift (4)
  • write (176-178)
  • write (188-212)
  • write (214-224)
  • write (226-237)
Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift (1)
Sources/StreamChat/Utils/KeyedDecodingContainer+Array.swift (1)
  • decodeArrayIfPresentIgnoringFailures (61-69)
Sources/StreamChat/Workers/ChannelUpdater.swift (7)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • setPushPreference (1698-1721)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • setPushPreference (464-480)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (3)
  • asModel (30-35)
  • asModel (58-60)
  • asModel (64-72)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (2)
  • asModel (33-35)
  • savePushPreference (53-59)
Sources/StreamChat/Database/DatabaseContainer.swift (4)
  • write (176-178)
  • write (188-212)
  • write (214-224)
  • write (226-237)
Sources/StreamChat/Database/DatabaseSession.swift (1)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1)
  • savePushPreference (53-59)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (3)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (2)
  • setPushPreference (1698-1721)
  • snoozePushNotifications (1727-1750)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (1)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1)
  • asModel (33-35)
Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (1)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
DemoApp/Screens/UserProfile/UserProfileViewController.swift (3)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (5)
  • updateUserData (251-280)
  • synchronize (182-202)
  • loadAllUnreads (440-446)
  • setPushPreference (464-480)
  • snoozePushNotifications (486-502)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (4)
  • updateUserData (22-60)
  • updateUserData (334-358)
  • loadAllUnreads (195-205)
  • setPushPreference (207-234)
DemoApp/Screens/AppConfigViewController/SwitchButton.swift (1)
  • didChangeValue (20-22)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
TestTools/StreamChatTestTools/Extensions/EndpoinPath+Equatable.swift (1)
  • _ (9-53)
DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (2)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (2)
  • setPushPreference (1698-1721)
  • snoozePushNotifications (1727-1750)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (2)
  • setPushPreference (464-480)
  • snoozePushNotifications (486-502)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift (1)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (2)
  • savePushPreference (53-59)
  • asModel (33-35)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (5)
Sources/StreamChat/Database/DTOs/ChannelDTO.swift (5)
  • channel (429-431)
  • asModel (508-510)
  • create (526-682)
  • load (150-153)
  • load (155-161)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (3)
  • asModel (30-35)
  • asModel (58-60)
  • asModel (64-72)
Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift (4)
  • asModel (227-227)
  • create (231-289)
  • load (50-61)
  • load (82-85)
Sources/StreamChat/Database/DTOs/NSManagedObject+Validation.swift (1)
  • isNotDeleted (19-22)
Sources/StreamChat/Utils/Database/NSManagedObject+Extensions.swift (1)
  • insertNewObject (14-22)
Sources/StreamChat/Database/DTOs/ChannelDTO.swift (2)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (2)
  • savePushPreference (53-59)
  • asModel (33-35)
Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (3)
  • asModel (10-74)
  • asModel (81-116)
  • asModel (122-129)
DemoApp/Screens/UserProfile/UnreadDetailsView.swift (1)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • unreads (41-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Build Test App and Frameworks
  • GitHub Check: Metrics
  • GitHub Check: Metrics
🔇 Additional comments (47)
Sources/StreamChat/Database/DatabaseSession.swift (1)

69-74: Public API addition documented and aligned

Signature and doc comment match the new Core Data helper, keeping the protocol contract coherent.

Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift (1)

18-19: Confirm non-queueable .pushPreferences
.pushPreferences is explicitly excluded from offline queuing (offline calls return immediately). Confirm this aligns with UX requirements—surface failures or defer actions—and ensure the endpoint remains idempotent for future queueing.

Sources/StreamChat/APIClient/Endpoints/Payloads/UserPayloads.swift (1)

33-34: LGTM; push_preferences is decoded in CurrentUserPayload and intentionally stripped from extraData. CurrentUserPayloads.swift decodes pushPreference; UserUpdateRequestBody excludes it.

Sources/StreamChat/Models/Payload+asModel/ChannelPayload+asModel.swift (1)

71-73: Resolved: pushPreference docs and DTO mapping verified
ChatChannel’s public API includes a documented pushPreference property, and ChannelDTO.asModel correctly wires pushPreference.

StreamChat.xcodeproj/project.pbxproj (19)

5743-5743: Core Data: ensure model/schema includes PushPreferenceDTO

Adding a DTO usually requires updating the xcdatamodeld (entities/attributes, migration options). Please confirm the model changes and lightweight migration are in place to avoid runtime crashes.


5933-5933: LGTM: Payloads group wiring

Entry looks correct in the Payloads group.


6092-6092: Public API docs reminder for new models

If PushPreference* types are public, add/verify doc comments in source per repo conventions.

Based on learnings


6968-6969: LGTM: New DemoApp group

UserProfile group addition in DemoApp is fine.


8688-8706: LGTM: New groups (PushPreferences, UserProfile)

Groups and children look consistent with the file refs.


11345-11345: LGTM: PushPreferencesView in DemoApp target

Included in Sources as expected for Demo UI.


11369-11369: LGTM: UnreadDetailsView in DemoApp target

Included in Sources as expected for Demo UI.


11600-11600: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreference.swift confined to core targets.


11624-11624: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreferenceLevel.swift confined to core targets.


11812-11812: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreferencePayloads.swift confined to core targets.


11949-11949: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreferenceDTO.swift confined to core targets.


12580-12580: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreferenceLevel.swift confined to core targets.


12663-12666: Same concern as earlier: target membership of core models

See the earlier comment about avoiding compiling PushPreferencePayloads.swift / PushPreference.swift into non-core targets.


12769-12769: Same concern as earlier: target membership of core models

See the earlier comment about keeping PushPreferenceDTO.swift confined to core targets.


13969-13969: DemoApp deployment target raised to iOS 15.6 — confirm necessity

If this bump is only needed for the DemoApp (e.g., new SwiftUI APIs), fine. Please confirm the framework targets’ minimum iOS remain unchanged to avoid an unintended SDK support drop.


13994-13994: Same as above: deployment target bump

Confirm intent and no impact on framework targets.


14020-14020: Same as above: deployment target bump

Confirm intent and no impact on framework targets.


4314-4319: Packaging includes new PushPreference files All new core files live in Sources/StreamChat, are part of the SwiftPM StreamChat target, included by StreamChat.podspec’s source_files glob, and referenced in project.pbxproj.


1431-1440: Verified target membership – no changes needed

PushPreference.swift, PushPreferenceLevel.swift, PushPreferencePayloads.swift and PushPreferenceDTO.swift are only in StreamChat and StreamChatStatic; UnreadDetailsView.swift and PushPreferencesView.swift are only in DemoApp.

Sources/StreamChat/APIClient/Endpoints/Payloads/ChannelListPayload.swift (3)

97-99: Decoding defaults/order: LGTM.

Using decodeIfPresent with a safe default for activeLiveLocations and optional pushPreference is consistent with existing patterns.


53-53: Approve channel-level pushPreference wiring
Verified pushPreference is decoded, persisted (DTOs), mapped in models, and surfaced in ChatChannel/UI.


79-80: No changes needed for pushPreference mapping
Server uses snake_case "push_preferences" in API responses.

DemoApp/Screens/UserProfile/UnreadDetailsView.swift (1)

11-11: Whitespace-only changes.

No action needed.

Also applies to: 15-15, 22-22, 62-62, 79-79, 111-111, 121-121, 132-132, 212-212, 216-216, 219-219, 234-234

Sources/StreamChat/Models/CurrentUser.swift (3)

64-66: Public API: pushPreference surfaced on CurrentChatUser — LGTM.

Docstring present as required.


105-105: Assignment: LGTM.

Property is correctly initialized.


92-94: CurrentUserDTO.asModel already maps pushPreference

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

482-502: Global snoozePushNotifications API: LGTM.

Correctly sets disabledUntil and preserves level semantics.


460-480: Global setPushPreference API: resolved. Verified that CurrentUserUpdater writes using a constant ID and retrieval occurs via the pushPreference relationship on CurrentUserDTO, so the hard-coded ID does not affect consistency.

DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (1)

7-7: Import SwiftUI: LGTM.

Required for hosting PushPreferencesView.

Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)

49-59: pushPreferences offline queue behavior verified
EndpointPath.pushPreferences returns false in shouldBeQueuedOffline, so this endpoint won’t be queued offline.

Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift (1)

100-101: No change required for pushPreferences path
The chat API prefixes endpoints with chat/, so returning "push_preferences" correctly yields chat/push_preferences.

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

218-220: Constructor parameter addition looks good.

Initializer wiring for pushPreference with a default is correct.

Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift (2)

83-84: Decoding path LGTM.

decodeIfPresent with the new key is correct and backward compatible.


21-23: No action needed: UserPayloadsCodingKeys already includes case pushPreference = "push_preferences".

Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1)

54-61: Confirm dropping dictionary keys is intentional.

UserPushPreferencesPayload.asModel() returns only values, discarding keys. If keys have semantic meaning (e.g., per-user or per-scope), this loses information.

Is the payload guaranteed to contain at most one relevant entry (e.g., current user), making the key irrelevant?

Sources/StreamChat/Database/DTOs/CurrentUserDTO.swift (2)

250-253: LGTM: Model mapping includes pushPreference.

Correctly materializes dto.pushPreference and threads it into CurrentChatUser.


254-288: Verify CurrentChatUser.pushPreference documentation
Unable to locate the CurrentChatUser definition to confirm if the new pushPreference parameter and property are documented. Ensure both are covered with inline doc comments per guidelines.

Sources/StreamChat/Models/PushPreferences/PushPreferenceLevel.swift (1)

7-25: Docs look good; confirm public API coverage.

Struct and cases are documented. If any new public property or init was added elsewhere (e.g., on models), ensure docs exist.

As per coding guidelines

Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1)

52-59: LGTM: save helper stores id, chatLevel, disabledUntil.

Consistent with payload mapping.

Sources/StreamChat/Database/DTOs/ChannelDTO.swift (1)

636-675: asModel mapping looks good.

pushPreference is bridged and passed through to ChatChannel correctly.

Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (3)

81-81: ChannelDTO.pushPreference relationship: LGTM.

Inverse and deletion rule are appropriate.


167-167: CurrentUserDTO.pushPreference relationship: LGTM.

Inverse and deletion rule are appropriate.


427-438: Enable lightweight migration in DatabaseContainer
No explicit shouldMigrateStoreAutomatically/shouldInferMappingModelAutomatically flags were found. Confirm these are set on your persistentStoreDescriptions (e.g. in DatabaseContainer.swift around lines 167–170) before calling loadPersistentStores.

@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Sep 26, 2025

SDK Size

title develop branch diff status
StreamChat 8.13 MB 8.25 MB +123 KB 🟢
StreamChatUI 4.88 MB 4.89 MB +16 KB 🟢

Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

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

Looks good ✅ There's just one retain cycle in the demo app, and of course the tests before merging.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
StreamChat.xcodeproj/project.pbxproj (1)

13973-13999: Undo the DemoApp deployment target bump to 15.6

Raising the DemoApp’s minimum iOS target from 14.3 straight to 15.6 drops support for every device still on iOS 14.x or 15.0–15.5 without a documented requirement for that jump. Please revert to the previous target (or justify a smaller, necessary bump tied to new APIs) so we don’t unnecessarily narrow the supported install base. Don’t forget to apply the same fix to the other build configurations modified in this hunk.

🧹 Nitpick comments (4)
TestTools/StreamChatTestTools/TestData/DummyData/UserPayload.swift (1)

45-77: Expose pushPreference in the dummy factory

The initializer now requires pushPreference, but the helper hardcodes nil. That blocks tests from constructing CurrentUserPayload cases with a non-nil push preference, which is exactly what the new feature needs. Please surface this as a parameter (defaulting to nil, same as privacySettings) so tests can exercise those paths.

     static func dummy(
         userId: UserId,
         name: String = .unique,
         imageUrl: URL? = .unique(),
         role: UserRole = .admin,
         teamsRole: [String: UserRole]? = nil,
         extraData: [String: RawJSON] = [:],
         teams: [TeamId] = [.unique, .unique, .unique],
         language: String? = nil,
         isBanned: Bool = false,
         updatedAt: Date = .unique,
         deactivatedAt: Date? = nil,
-        privacySettings: UserPrivacySettingsPayload? = nil
+        privacySettings: UserPrivacySettingsPayload? = nil,
+        pushPreference: PushPreferencePayload? = nil
     ) -> CurrentUserPayload {
         .init(
             id: userId,
             name: name,
             imageURL: imageUrl,
             role: role,
             teamsRole: teamsRole,
             createdAt: .unique,
             updatedAt: updatedAt,
             deactivatedAt: deactivatedAt,
             lastActiveAt: .unique,
             isOnline: true,
             isInvisible: true,
             isBanned: isBanned,
             teams: teams,
             language: language,
             extraData: extraData,
             privacySettings: privacySettings,
-            pushPreference: nil
+            pushPreference: pushPreference
         )
     }
Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift (1)

391-394: Use the generated user ID when seeding push preferences

We already generate currentUserId; reusing it here keeps the seeded push preference consistent with the rest of the fixture data. That avoids inserting a dangling preference tied to the literal string "currentUserId".

-            try session.savePushPreference(id: "currentUserId", payload: .init(chatLevel: "mentions", disabledUntil: nil))
+            try session.savePushPreference(id: currentUserId, payload: .init(chatLevel: "mentions", disabledUntil: nil))
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (1)

214-216: Expose pushPreference in DM/NonDM mocks

mockDMChannel always forces pushPreference to nil, which makes it impossible to exercise the new push-preference flows when using this helper. Please thread the (optional) parameter through here (and mirror the change in mockNonDMChannel) so tests relying on these convenience mocks can configure the new field.

     activeLiveLocations: [SharedLocation] = [],
+    pushPreference: PushPreference? = nil
 ) -> Self {
     self.init(
         …
-        activeLiveLocations: activeLiveLocations,
-        pushPreference: nil
+        activeLiveLocations: activeLiveLocations,
+        pushPreference: pushPreference
     )
 }

Apply the same pattern to mockNonDMChannel.

Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift (1)

457-471: Consider adding an asModel assertion for push preference.

We now rely on ChannelPayload.asModel to surface pushPreference onto ChatChannel, but this test still exercises the nil path. A small follow-up test with a non-nil payload would verify the mapping end-to-end.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6baaf7 and b9542bc.

📒 Files selected for processing (20)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift (3 hunks)
  • StreamChat.xcodeproj/project.pbxproj (21 hunks)
  • TestTools/StreamChatTestTools/Fixtures/JSONs/Channel.json (1 hunks)
  • TestTools/StreamChatTestTools/Fixtures/JSONs/CurrentUser.json (1 hunks)
  • TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift (4 hunks)
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (10 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (2 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/CurrentUserPayload.swift (4 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/UserPayload.swift (1 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (4 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (2 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift (4 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift (1 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift (1 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift (1 hunks)
  • Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift (9 hunks)
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift (2 hunks)
  • Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift (1 hunks)
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/CurrentUserPayloads.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • TestTools/StreamChatTestTools/Fixtures/JSONs/CurrentUser.json
🧰 Additional context used
📓 Path-based instructions (3)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/CurrentUserPayload.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/UserPayload.swift
  • TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift
Tests/StreamChatTests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover core models and API surface of StreamChat

Files:

  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift
  • Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift
  • Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift
🧠 Learnings (4)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatTests/**/*.swift : Ensure tests cover core models and API surface of StreamChat

Applied to files:

  • Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
  • Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Prefer using provided test fakes/mocks in tests when possible

Applied to files:

  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatChannel_Mock.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
  • TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Add or extend tests in the matching module’s Tests folder

Applied to files:

  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • StreamChat.xcodeproj/project.pbxproj
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

  • Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift
  • Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift
🧬 Code graph analysis (7)
Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (1)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
Tests/StreamChatTests/APIClient/Endpoints/Payloads/PushPreferencePayload_Tests.swift (1)
TestTools/StreamChatTestTools/Extensions/String+Date.swift (1)
  • toDate (10-17)
Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift (2)
TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (1)
  • dummy (11-41)
TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (1)
  • dummy (11-58)
Tests/StreamChatTests/Database/DatabaseContainer_Tests.swift (1)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
  • savePushPreference (132-134)
Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift (1)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (7)
Sources/StreamChat/Database/DTOs/ChannelDTO.swift (1)
  • saveChannelList (218-235)
Sources/StreamChat/Database/DTOs/ReactionListQueryDTO.swift (1)
  • saveQuery (53-77)
Sources/StreamChat/Database/DTOs/MessageReminderDTO.swift (1)
  • saveReminder (107-143)
Sources/StreamChat/Database/DTOs/UserDTO.swift (1)
  • saveUsers (183-189)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (5)
  • saveMessages (1163-1178)
  • saveMessageSearch (1541-1546)
  • saveMessage (944-1159)
  • saveMessage (1180-1224)
  • saveMessage (1326-1330)
Sources/StreamChat/Database/DTOs/MessageReactionDTO.swift (1)
  • saveReactions (138-150)
Sources/StreamChat/Database/DTOs/MemberModelDTO.swift (1)
  • saveMembers (160-173)
Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift (1)
TestTools/StreamChatTestTools/Extensions/String+Date.swift (1)
  • toDate (10-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Test App and Frameworks
  • GitHub Check: Build LLC + UI (Xcode 15)
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Automated Code Review
  • GitHub Check: Metrics
🔇 Additional comments (16)
Tests/StreamChatTests/APIClient/Endpoints/Payloads/IdentifiablePayload_Tests.swift (1)

409-410: Initializer update handled correctly.

Thanks for wiring the new pushPreference parameter (and keeping activeLiveLocations explicit) into the fixture builder—this keeps the helper aligned with the updated ChannelPayload signature.

Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift (1)

78-85: Nice coverage for the new endpoint path.

These assertions guard both the offline queue behaviour and the raw path string, so regressions in the new push-preferences endpoint will get caught early.

TestTools/StreamChatTestTools/Fixtures/JSONs/Channel.json (1)

1608-1612: Confirm the payload key name.

The fixture uses push_preference (singular). Please double-check this matches the coding key in ChannelPayload (which, per the implementation summary, might expect "push_preferences"). A mismatch here would keep the decoded value nil, undermining the new tests. Let's align the JSON key with the production payload.

Tests/StreamChatTests/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware_Tests.swift (1)

56-58: Initializer update looks correct.

Good call threading the new pushPreference parameter through the channel payload setup so the test scaffold keeps compiling.

Tests/StreamChatTests/APIClient/Endpoints/Payloads/CurrentUserPayloads_Tests.swift (1)

46-48: Fixture assertion keeps the new field honest.

Verifying both the level and disabledUntil timestamp against the fixture will surface decoding regressions quickly.

Tests/StreamChatTests/APIClient/Endpoints/Payloads/ChannelListPayload_Tests.swift (2)

350-353: Great to see the fixture asserting push preferences.

This ensures the decoding path stays wired up once the backend starts returning the field.


563-565: Initializer update keeps the helpers aligned.

Thanks for propagating the new parameter into the minimal payload helper to prevent accidental churn later.

Tests/StreamChatTests/APIClient/Endpoints/UserEndpoints_Tests.swift (1)

87-121: Endpoint contract well covered.

The test exercises the body, HTTP verb, and connection requirements, so the new push-preference endpoint should stay stable.

Tests/StreamChatTests/Database/DTOs/CurrentUserDTO_Tests.swift (1)

73-76: End-to-end round-trip looks solid.

Setting the payload and then asserting on the model level (with tolerant date comparison) gives confidence that persistence and mapping handle the new preference correctly.

Also applies to: 111-112

TestTools/StreamChatTestTools/TestData/DummyData/ChannelPayload.swift (1)

23-40: Dummy payload now exposes push preference

Adding the optional pushPreference with a sensible default keeps existing call sites intact while letting the tests opt into the new data. Nice and tidy.

Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift (2)

350-356: Great coverage for push preference persistence

The round‑trip assertion guarantees the new push preference survives the save/load cycle and matches the stored level/expiry exactly—thanks for threading it through this core integration test.

Also applies to: 483-489


1134-1136: Initializer updates stay aligned

Appreciate the sweep across every direct ChannelPayload construction to pass the fresh pushPreference slot—keeps the fixtures compiling and explicit about intent.

Also applies to: 1193-1195, 1554-1565, 1614-1620, 1645-1647, 1689-1700, 1754-1758

TestTools/StreamChatTestTools/TestData/DummyData/XCTestCase+Dummy.swift (3)

29-31: Current-user dummy mirrors production shape

Including pushPreference in the canned current-user payload ensures the higher-level tests can exercise the new API surface without extra boilerplate.


164-216: Channel dummy handles optional push preference cleanly

Making channelReads optional and piping the optional pushPreference straight into the payload keeps the helper flexible while preserving the previous default behaviour.


334-336: No-extra-data payload stays compatible

Explicitly setting pushPreference: nil here keeps this stripped-down builder aligned with the broadened initializer—thanks for catching it.

TestTools/StreamChatTestTools/TestData/DummyData/CurrentUserPayload.swift (1)

29-53: Current-user factories carry push preference through

Both dummy constructors forwarding the optional push preference (and privacy settings) keeps the helpers future-proof for the new APIs.

Also applies to: 64-88

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (1)

907-937: Verify the DB write and returned preference

The test name promises we persist the push preference, yet we only check that the completion fires without an error. A regression where savePushPreference stops writing (or the returned PushPreference no longer matches the payload) would sail through. Please assert that the spy recorded the write with the expected payload or, even better, read the stored PushPreference back from the DB view context and compare the level/disabledUntil to the request, and also assert that the completion handed back the same model that the worker produced. Based on learnings.

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

2233-2266: Tighten the “savesToDatabase” assertion

Similar to the user-updater test, we only verify that the completion is called. To make this test meaningful, please assert that the database spy captured a savePushPreference call (or read the DTO back via database.viewContext) and that the PushPreference delivered to the completion matches the expected level/disabledUntil. Without that, a regression on persistence or payload mapping will go unnoticed. Based on learnings.

Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift (2)

898-934: Assert callback queue consistency for setPushPreference

In other controller tests we verify that completions are dispatched on callbackQueue. Here we only capture the result, so we’d miss a regression where callback { … } is removed from the production code. Please add the usual AssertTestQueue(withId: callbackQueueID) inside the completion (same for the failure branch) so we keep the scheduling contract covered.


956-993: Mirror the callback-queue assertion for snoozePushNotifications

Same rationale as above: add AssertTestQueue(withId: callbackQueueID) inside the success and failure closures so that we continue to guard the callback-queue guarantee for the snooze API.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b9542bc and 2366657.

📒 Files selected for processing (7)
  • Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1 hunks)
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (3 hunks)
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (3 hunks)
  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift (1 hunks)
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
  • Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift
Tests/{StreamChatTests,StreamChatUITests}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
Tests/StreamChatTests/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Ensure tests cover core models and API surface of StreamChat

Files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
Sources/{StreamChat,StreamChatUI}/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

When altering public API, update inline documentation comments in source

Files:

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

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/StreamChatUITests/**/*.swift : Ensure tests cover view controllers and UI behaviors of StreamChatUI

Applied to files:

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

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to Tests/{StreamChatTests,StreamChatUITests}/**/*.swift : Prefer using provided test fakes/mocks in tests when possible

Applied to files:

  • Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
  • Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift
  • Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift
  • TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
  • Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift
🧬 Code graph analysis (7)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
  • setPushPreference (602-611)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (2)
  • setPushPreference (1698-1721)
  • snoozePushNotifications (1727-1750)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift (4)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
  • setPushPreference (602-611)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (1)
  • test_simulateResponse (108-111)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
  • setPushPreference (602-611)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • setPushPreference (464-480)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Tests/StreamChatTests/Workers/CurrentUserUpdater_Tests.swift (7)
Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift (3)
  • test_setPushPreference_makesCorrectAPICall (2215-2231)
  • test_setPushPreference_successfulResponse_savesToDatabase (2233-2266)
  • test_setPushPreference_propagatesNetworkError (2268-2291)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • setPushPreference (103-110)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (1)
  • setPushPreference (464-480)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
Sources/StreamChat/APIClient/Endpoints/UserEndpoints.swift (1)
  • pushPreferences (49-59)
TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (1)
  • test_simulateResponse (108-111)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (5)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • setPushPreference (103-110)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (1)
  • setPushPreference (207-234)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • setPushPreference (1698-1721)
Sources/StreamChat/Workers/ChannelUpdater.swift (1)
  • setPushPreference (722-748)
TestTools/StreamChatTestTools/Extensions/Result+Extensions.swift (2)
  • invoke (8-10)
  • invoke (14-21)
Tests/StreamChatTests/Controllers/CurrentUserController/CurrentUserController_Tests.swift (2)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)
  • setPushPreference (103-110)
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift (2)
  • setPushPreference (464-480)
  • snoozePushNotifications (486-502)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1)
Sources/StreamChat/Database/DTOs/PushPreferenceDTO.swift (1)
  • asModel (33-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Automated Code Review
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Build Test App and Frameworks
  • GitHub Check: Build LLC + UI (Xcode 15)
  • GitHub Check: Metrics
🔇 Additional comments (3)
Sources/StreamChat/APIClient/Endpoints/Payloads/PushPreferencePayloads.swift (1)

71-80: Guard against duplicate channel preferences crashing the payload parser.

Dictionary(uniqueKeysWithValues:) traps when the flattened payload happens to contain the same channel CID twice, so a bad server payload would take the SDK down. Please merge manually (last write wins) instead of relying on the initializer.

-        .init(uniqueKeysWithValues: values
-            .flatMap { $0 }
-            .compactMap { key, value in
-                guard let channelId = try? ChannelId(cid: key) else { return nil }
-                return (channelId, value.asModel())
-            }
-        )
+        values
+            .flatMap { $0 }
+            .reduce(into: [ChannelId: PushPreference]()) { result, element in
+                guard let channelId = try? ChannelId(cid: element.key) else { return }
+                result[channelId] = element.value.asModel()
+            }
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)

160-164: Push preference mock wiring looks solid

The new atomic slots, cleanup reset, and override mirror the production signature, so tests can capture parameters and drive completions deterministically. Nicely aligned with the other updater mocks.

Also applies to: 306-309, 602-610

TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/CurrentUserUpdater_Mock.swift (1)

40-43: Good parity with the production push preference API

Capturing the payload/completion and exposing a stubbed result keeps the current-user mock consistent with the channel mock, which should make the new push preference tests straightforward.

Also applies to: 103-110, 141-143

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 35c2a3d and c5648a6.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Update CHANGELOG for user-visible SDK changes

Files:

  • CHANGELOG.md
🧠 Learnings (1)
📚 Learning: 2025-09-18T10:00:24.878Z
Learnt from: CR
PR: GetStream/stream-chat-swift#0
File: AGENTS.md:0-0
Timestamp: 2025-09-18T10:00:24.878Z
Learning: Applies to CHANGELOG.md : Update CHANGELOG for user-visible SDK changes

Applied to files:

  • CHANGELOG.md
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md

9-9: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


10-10: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


11-11: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


12-12: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test LLC (Debug)
  • GitHub Check: Build LLC + UI (Xcode 15)
  • GitHub Check: Automated Code Review
  • GitHub Check: Metrics

Copy link

sonarqubecloud bot commented Sep 27, 2025

@nuno-vieira nuno-vieira added the 🤞 Ready For QA A PR that is Ready for QA label Sep 27, 2025
@testableapple testableapple added 🧪 QAing 🟢 QAed A PR that was QAed and removed 🤞 Ready For QA A PR that is Ready for QA 🧪 QAing labels Sep 29, 2025
@nuno-vieira nuno-vieira merged commit 7bba56b into develop Sep 29, 2025
21 of 23 checks passed
@nuno-vieira nuno-vieira deleted the add/push-pref branch September 29, 2025 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🟢 QAed A PR that was QAed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants