I am trying to return the search result when user type into search bar . Here I am using combine approach with Swift UI . I am trying to binding the object form view model on change function but I am getting following errors.
Cannot convert value of type 'Published.Publisher' to expected argument type '(String) -> Void'
Here is my viewModel code .
final class FruitListViewModel: FruitListViewModelType, ObservableObject {
private let service: Service!
@Published private(set) var fruits = [Fruits]()
// @Published var filteredFruit: [Fruits] = []
private var cancellable = Set<AnyCancellable>()
@Published var searchText: String = ""
init(service:Service = ServiceImpl()) {
self.service = service
}
func fetchFruit() {
let client = ServiceClient(baseUrl:EndPoints.baseUrl.rawValue, path:Path.fruitList.rawValue, params:"", method:"get")
service.fetchData(client: client, type: [Fruits].self)
.receive(on: RunLoop.main)
.sink { completion in
switch completion {
case let .failure(error):
print(error)
default:
break
}
} receiveValue: { response in
self.fruits = response
}.store(in: &cancellable)
$searchText
.combineLatest($fruits)
.debounce(for: .milliseconds(80), scheduler: RunLoop.main)
.map { (searchText, fruit) -> [Fruits] in
guard !searchText.isEmpty else {
return fruit
}
let lowercasedText = searchText.lowercased()
let filterFruitList = fruit.filter { (fruit) -> Bool in
fruit.name.lowercased().contains(lowercasedText) ||
fruit.genus.lowercased().contains(lowercasedText) ||
fruit.family.lowercased().contains(lowercasedText)
}
return filterFruitList
}
.sink { [weak self] (filterList) in
self?.fruits = filterList
}
.store(in: &cancellable)
}
}
Here is the code into view ..
struct ContentView: View {
@EnvironmentObject private var viewModel: FruitListViewModel
@State var searchText = ""
var body: some View {
NavigationView {
List {
ForEach(viewModel.fruits) { fruit in
NavigationLink(destination: FruitDetailsView(fruit: fruit)) {
RowView(name: fruit.name, genus: fruit.genus, family: fruit.family)
}
}
}
.searchable(text: $viewModel.searchText)
.onChange(of: viewModel.searchText, perform: viewModel.$searchText)
.task {
viewModel.fetchFruit()
}
.navigationTitle("Fruits List")
}
.onAppear {
viewModel.fetchFruit()
}
}
}
Here is the screenshot of the error ..
Search filter result .
Replace
@Published private(set) var fruits = [Fruits]()
// @Published var filteredFruit: [Fruits] = []
with
@Published private(set) var fruits = [Fruits]()
@Published var filteredFruit: [Fruits] = []
Replace
.sink { [weak self] (filterList) in
self?.fruits = filterList
}
with
.sink { [weak self] (filterList) in
self?.filteredFruit = filterList
}
Replace
struct ContentView: View {
@EnvironmentObject private var viewModel: FruitListViewModel
@State var searchText = ""
var body: some View {
NavigationView {
List {
ForEach(viewModel.fruits) { fruit in
NavigationLink(destination: FruitDetailsView(fruit: fruit)) {
RowView(name: fruit.name, genus: fruit.genus, family: fruit.family)
}
}
}
.searchable(text: $viewModel.searchText)
.onChange(of: viewModel.searchText, perform: viewModel.$searchText)
.task {
viewModel.fetchFruit()
}
.navigationTitle("Fruits List")
}
.onAppear {
viewModel.fetchFruit()
}
}
}
with
struct ContentView: View {
@EnvironmentObject private var viewModel: FruitListViewModel
var body: some View {
NavigationView {
List {
ForEach(viewModel.filteredFruit) { fruit in
NavigationLink(destination: FruitDetailsView(fruit: fruit)) {
RowView(name: fruit.name, genus: fruit.genus, family: fruit.family)
}
}
}
.searchable(text: $viewModel.searchText)
.navigationTitle("Fruits List")
}
.onAppear {
viewModel.fetchFruit()
}
}
}
And (not related) declare
private let service: Service // no exclamation mark
Side note:
Please consolidate the fruit(s)
spelling, there is no plural form with trailing s
in English and also the model struct is supposed to be named Fruit
.