@@ -2,6 +2,7 @@ import SwiftUI
22import WordPressData
33import WordPressUI
44import WordPressShared
5+ import CoreData
56
67struct ReaderSubscriptionsView : View {
78 @FetchRequest (
@@ -13,14 +14,14 @@ struct ReaderSubscriptionsView: View {
1314 @State private var searchText = " "
1415 @State private var isShowingMainAddSubscriptonPopover = false
1516
16- @State private var searchResults : [ ReaderSiteTopic ] = [ ]
17+ @State private var searchResults : [ ReaderSiteTopic ] ?
18+ @State private var searchTask : Task < Void , Never > ?
19+ @State private var pendingSearchText : String ?
1720
1821 @StateObject private var viewModel = ReaderSubscriptionsViewModel ( )
1922
2023 @Environment ( \. horizontalSizeClass) private var horizontalSizeClass
2124
22- var isShowingSearchResuts : Bool { !searchText. isEmpty }
23-
2425 var onSelection : ( _ subscription: ReaderSiteTopic ) -> Void = { _ in }
2526
2627 var body : some View {
@@ -72,7 +73,7 @@ struct ReaderSubscriptionsView: View {
7273
7374 private var main : some View {
7475 List {
75- if isShowingSearchResuts {
76+ if let searchResults {
7677 ForEach ( searchResults, id: \. objectID, content: makeSubscriptionCell)
7778 . onDelete ( perform: delete)
7879 } else {
@@ -84,11 +85,11 @@ struct ReaderSubscriptionsView: View {
8485 . searchable ( text: $searchText)
8586 . onReceive ( subscriptions. publisher) { _ in
8687 if !searchText. isEmpty {
87- reloadSearchResults ( searchText: searchText)
88+ performBackgroundSearch ( searchText: searchText)
8889 }
8990 }
9091 . onChange ( of: searchText) {
91- reloadSearchResults ( searchText: $0)
92+ performBackgroundSearch ( searchText: $0)
9293 }
9394 }
9495
@@ -117,7 +118,7 @@ struct ReaderSubscriptionsView: View {
117118 }
118119
119120 private func getSubscription( at index: Int ) -> ReaderSiteTopic {
120- if isShowingSearchResuts {
121+ if let searchResults {
121122 searchResults [ index]
122123 } else {
123124 subscriptions [ index]
@@ -128,9 +129,66 @@ struct ReaderSubscriptionsView: View {
128129 ReaderSubscriptionHelper ( ) . unfollow ( site)
129130 }
130131
131- private func reloadSearchResults( searchText: String ) {
132- let ranking = StringRankedSearch ( searchTerm: searchText)
133- searchResults = ranking. search ( in: subscriptions) { " \( $0. title) \( $0. siteURL) " }
132+ private func performBackgroundSearch( searchText: String ) {
133+ struct SearchableSubscription : Sendable {
134+ let objectID : NSManagedObjectID
135+ let title : String
136+ let siteURL : String
137+
138+ var searchableText : String {
139+ " \( title) \( siteURL) "
140+ }
141+
142+ init ( _ subscription: ReaderSiteTopic ) {
143+ self . objectID = subscription. objectID
144+ self . title = subscription. title
145+ self . siteURL = subscription. siteURL
146+ }
147+ }
148+
149+ // Cancel any existing search task
150+ searchTask? . cancel ( )
151+
152+ // Clear results immediately if search text is empty
153+ if searchText. isEmpty {
154+ searchResults = nil
155+ pendingSearchText = nil
156+ return
157+ }
158+
159+ // Start new background search task
160+ searchTask = Task {
161+ // Store the search text we're processing
162+ let currentSearchText = searchText
163+
164+ // Create searchable data on main thread to avoid Core Data context issues
165+ let searchableData = subscriptions. map ( SearchableSubscription . init)
166+
167+ // Perform the search on a background queue with parallel processing
168+ let resultObjectIDs = await StringRankedSearch ( searchTerm: currentSearchText)
169+ . parallelSearch ( in: searchableData) { $0. searchableText }
170+ . map ( \. objectID)
171+
172+ // Check if we were cancelled or if search text changed during search
173+ guard !Task. isCancelled else { return }
174+
175+ // Update results on main thread
176+ await MainActor . run {
177+ // Only update if this search is still relevant
178+ if currentSearchText == searchText {
179+ searchResults = subscriptions. filter { resultObjectIDs. contains ( $0. objectID) }
180+ pendingSearchText = nil
181+ } else {
182+ // Search text changed during our search, mark that we need a new search
183+ pendingSearchText = searchText
184+ }
185+
186+ // If there's a pending search, start it now
187+ if let pendingSearchText {
188+ performBackgroundSearch ( searchText: pendingSearchText)
189+ }
190+ }
191+ }
134192 }
135193}
136194
0 commit comments