-
Notifications
You must be signed in to change notification settings - Fork 233
Support predefined filters in ChannelListQuery #4113
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
Changes from 12 commits
82fef7e
34de19d
160479f
e444cfa
6fc2f12
440e2da
b0a5371
7f4a230
4634878
cfce125
e42cc6d
674dd4b
29110a9
b959c98
81fc21a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |
|
|
||
| # Upcoming | ||
|
|
||
| ### π Changed | ||
| ## StreamChat | ||
| ### β Added | ||
| - Add `ChannelListQuery(predefinedFilter:filterValues:sortValues:)` for creating channel list queries with predefined filters [#4113](https://github.com/GetStream/stream-chat-swift/pull/4113) | ||
|
|
||
|
Comment on lines
+6
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align Use As per coding guidelines, "Follow Keep a Changelog format with π€ Prompt for AI Agents |
||
| # [5.4.0](https://github.com/GetStream/stream-chat-swift/releases/tag/5.4.0) | ||
| _May 28, 2026_ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| sources: | ||
| - ./Query/ChannelListQuery.swift | ||
| - ./Query/Sorting/ChannelListSortingKey.swift | ||
| templates: | ||
| - ./Generated/PredefinedFilter.stencil | ||
| output: | ||
| ./Generated/PredefinedFilter+Generated.swift |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,7 @@ extension ChatClient { | |
| /// - Note: For an async-await alternative of the `ChatChannelListController`, please check ``ChannelList`` in the async-await supported [state layer](https://getstream.io/chat/docs/sdk/ios/client/state-layer/state-layer-overview/). | ||
| public class ChatChannelListController: DataController, DelegateCallable, DataStoreProvider, @unchecked Sendable { | ||
| /// The query specifying and filtering the list of channels. | ||
| public let query: ChannelListQuery | ||
| public internal(set) var query: ChannelListQuery | ||
|
|
||
| /// The `ChatClient` instance this controller belongs to. | ||
| public let client: ChatClient | ||
|
|
@@ -82,8 +82,15 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| } | ||
|
|
||
| private(set) lazy var channelListObserver: BackgroundListDatabaseObserver<ChatChannel, ChannelDTO> = { | ||
| if let updated = worker.loadPredefinedFilter(for: query) { | ||
| query = updated | ||
| } | ||
|
Comment on lines
+85
to
+87
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For supporting app relaunches where API call has not finished and we load the filter from CoreData and pass it to FRC.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had 2 options here: |
||
| return makeChannelListObserver() | ||
| }() | ||
|
|
||
| private func makeChannelListObserver() -> BackgroundListDatabaseObserver<ChatChannel, ChannelDTO> { | ||
| let request = ChannelDTO.channelListFetchRequest(query: self.query, chatClientConfig: client.config) | ||
| let observer = self.environment.createChannelListDatabaseObserver( | ||
| let observer = environment.createChannelListDatabaseObserver( | ||
| client.databaseContainer, | ||
| request, | ||
| { try $0.asModel() }, | ||
|
|
@@ -101,7 +108,7 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| } | ||
| } | ||
| return observer | ||
| }() | ||
| } | ||
|
|
||
| var _basePublishers: Any? | ||
| /// An internal backing object for all publicly available Combine publishers. We use it to simplify the way we expose | ||
|
|
@@ -146,11 +153,8 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| startChannelListObserverIfNeeded() | ||
| channelListLinker.start(with: client.eventNotificationCenter) | ||
| client.syncRepository.startTrackingChannelListController(self) | ||
| updateChannelList { [weak self] error in | ||
| guard let completion else { return } | ||
| self?.callback { | ||
| completion(error) | ||
| } | ||
| updateChannelList { [weak self] result in | ||
| self?.callback { completion?(result.error) } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -179,9 +183,9 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| updatedQuery.pagination = Pagination(pageSize: limit, offset: channels.count) | ||
| worker.update(channelListQuery: updatedQuery) { result in | ||
| switch result { | ||
| case let .success(channels): | ||
| self.markChannelsAsDeliveredIfNeeded(channels: channels) | ||
| self.hasLoadedAllPreviousChannels = channels.count < limit | ||
| case let .success(updateResult): | ||
| self.markChannelsAsDeliveredIfNeeded(channels: updateResult.channels) | ||
| self.hasLoadedAllPreviousChannels = updateResult.channels.count < limit | ||
| self.callback { completion?(nil) } | ||
| case let .failure(error): | ||
| self.callback { completion?(error) } | ||
|
|
@@ -199,24 +203,30 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| // MARK: - Helpers | ||
|
|
||
| private func updateChannelList( | ||
| _ completion: (@MainActor (_ error: Error?) -> Void)? = nil | ||
| _ completion: (@MainActor (Result<ChannelListUpdateResult, Error>) -> Void)? = nil | ||
| ) { | ||
| let limit = query.pagination.pageSize | ||
| worker.update( | ||
| channelListQuery: query | ||
| ) { [weak self] result in | ||
| switch result { | ||
| case let .success(channels): | ||
| case let .success(updateResult): | ||
| self?.state = .remoteDataFetched | ||
| self?.hasLoadedAllPreviousChannels = channels.count < limit | ||
| self?.hasLoadedAllPreviousChannels = updateResult.channels.count < limit | ||
|
|
||
| // Mark channels as delivered if synchronization was successful | ||
| self?.markChannelsAsDeliveredIfNeeded(channels: channels) | ||
| self?.markChannelsAsDeliveredIfNeeded(channels: updateResult.channels) | ||
|
|
||
| // Predefined filters can update local query representation (query gets backend defined filter and sort which must be set to FRC) | ||
| if let updatedQuery = updateResult.updatedQuery { | ||
| self?.query = updatedQuery | ||
| self?.updateChannelListObserver() | ||
| } | ||
|
|
||
| self?.callback { completion?(nil) } | ||
| self?.callback { completion?(.success(updateResult)) } | ||
| case let .failure(error): | ||
| self?.state = .remoteDataFetchFailed(ClientError(with: error)) | ||
| self?.callback { completion?(error) } | ||
| self?.callback { completion?(.failure(error)) } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -264,6 +274,16 @@ public class ChatChannelListController: DataController, DelegateCallable, DataSt | |
| log.error("Failed to perform fetch request with error: \(error). This is an internal error.") | ||
| } | ||
| } | ||
|
|
||
| private func updateChannelListObserver() { | ||
| channelListObserver = makeChannelListObserver() | ||
| do { | ||
| try channelListObserver.startObserving() | ||
| } catch { | ||
| state = .localDataFetchFailed(ClientError(with: error)) | ||
| log.error("Failed to update the channel list observer: \(error)") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| extension ChatChannelListController { | ||
|
|
||
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.
π₯