我想在搜索栏中应用去抖或延迟,并允许用户在几毫秒内键入文本并根据用户在搜索栏中的搜索文本来过滤数据。我已尝试将下面的代码放入 searchResults Completed 属性中,但实际上不允许我这样做。
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
实现它的替代方法是什么?
这是我的视图模型代码...
import Foundation
enum ViewState {
case loading
case error
case loaded
case emptyView
}
protocol PeopleListViewModelAction: ObservableObject {
func getPeopleList(urlStr: String) async
}
// MARK: - People List ViewModel Implementation.
@MainActor
final class PeopleListViewModel {
@Published private(set) var viewState: ViewState = .loaded
@Published var searchText = ""
private(set) var filteredPeople: [PeopleData] = []
private(set) var peopleLists: [PeopleData] = []
private let repository: PeopleCardsRepository
init(repository: PeopleCardsRepository) {
self.repository = repository
}
}
extension PeopleListViewModel: PeopleListViewModelAction {
func getPeopleList(urlStr: String) async {
viewState = .loaded
guard let url = URL(string: urlStr) else { return }
do {
let lists = try await repository.getPeopleList(for: url)
peopleLists = lists
viewState = peopleLists.isEmpty ? .emptyView : .loaded
} catch {
viewState = .error
}
}
}
extension PeopleListViewModel {
var searchResults: [PeopleData] {
guard !searchText.isEmpty else { return peopleLists }
return peopleLists.filter { $0.firstName.localizedStandardContains(searchText) }
}
}
这是我的查看代码...
import SwiftUI
struct PeopleListView: View {
// MARK: - Using State Object to make sure view model object will not destroyed or recreate.
@StateObject var viewModel: PeopleListViewModel
@State private var isErrorOccured = true
var body: some View {
NavigationStack {
VStack {
switch viewModel.viewState {
case .loading:
ProgressView()
case .loaded:
showPeopleListView()
case .error:
showErrorView()
case .emptyView:
EmptyView()
}
}
.navigationTitle(Text(LocalizedStringKey("People List")))
.searchable(text: $viewModel.searchText, prompt: "Please enter name..")
}.task {
await viewModel.getPeopleList(urlStr: Endpoint.peopleListURL)
}
}
@ViewBuilder
func showPeopleListView() -> some View {
List(viewModel.searchResults) { peopleList in
NavigationLink {
PeopleDetailsView(people: peopleList)
}label: {
PeopleCellView(people: peopleList)
}
}
}
@ViewBuilder
func showErrorView() -> some View {
ProgressView().alert(isPresented: $isErrorOccured) {
Alert(title: Text("Error Occured"),message: Text("Something went wrong"),
dismissButton: .default(Text("Ok")))
}
}
}
#Preview {
PeopleListView(viewModel: PeopleListViewModel(repository: PeopleRepositoryImplementation(networkManager: NetworkManager())))
}
这是错误的屏幕截图..
这是应用程序的屏幕截图:
.task
旨在取代合并的 @StateObject
,在异步/等待中去抖动它非常简单,只是 Task.sleep
,例如尝试这样的事情:
@State var result: Result<[People], Error>?
..
.task(id: searchText) {
do {
try Task.sleep(for: .seconds(0.5)) // debounce
//either let peopleController = peopleController() or better @Environment(\.peopleController) peopleController
let people = await peopleController.getPeopleList(searchText: searchText)
result = .success(people)
}
catch {
result = .failure(error)
}
}