0

I have an array of objects and I'd like to filter out the list based on the search text. This is what I have so far:

CountryListView:

func getFilteredData(data: [CountryStats], searchText: String) -> [CountryStats] {
    if searchText.isEmpty == false {
        return data.filter { $0.country == searchText }
    }
    else {
        return data
    }
}


struct CountryMasterView: View {

    @Environment(\.colorScheme) var colorScheme
    @State private var searchText: String = ""
    var countryStats: [CountryStats]

    var body: some View {
        NavigationView {
            VStack {
                SearchBar(text: $searchText)
             List {
                ForEach(countryStats.filter {
                    self.searchText.isEmpty ? true : getFilteredData(data: countryStats, searchText: searchText)
                }, id: \.self) { stat in
                if stat.country as AnyObject !== "All" as AnyObject {

                    NavigationLink(destination: CountryDetailView(countryStats: stat)) {
                        HStack {
                            URLImage(URL(string: stat.flag ?? "")!) { proxy in
                            proxy.image
                                .resizable()
                                .scaledToFit()
                                .clipped()

                            }
                                .frame(width: 50.0, height: 35.0)

                            VStack(alignment: .leading) {
                                Text(stat.country)
                                    .font(.headline)
                                Text("Total cases: \(stat.cases)")
                                    .font(.subheadline)
                            }
                        }

                  }
                }

                }


            }.navigationBarTitle("Cases By Country")
        }
        }
    }
}

This is my Model:

struct AllCountryStats: Identifiable, Decodable {
    let id = UUID()
    var data: [CountryStats]
    let message: String
}

struct CountryStats: Identifiable, Decodable, Hashable {
    let id = UUID()
    let active, cases: Int
    let country: String
    let deaths, recovered, todayCases, todayDeaths: Int
    let flag: String?
    let updated: Double
}

And this is the search view:

struct SearchBar: UIViewRepresentable {
    @Binding var text: String

    class Coordinator: NSObject, UISearchBarDelegate {
        @Binding var text: String

        init(text: Binding<String>) {
            _text = text
        }

        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            text = searchText
        }

    }

    func makeCoordinator() -> SearchBar.Coordinator {
        return Coordinator(text: $text)
    }

    func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator
        searchBar.searchBarStyle = .minimal
        return searchBar
    }

    func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
        uiView.text = text
    }

}

So, I'd like to have a search bar to filter out the countries.

Is there something I am missing or am I doing it the wrong way? I get the error Unable to infer complex closure return type; add explicit type to disambiguate

1 Answer 1

1

There are some problems with your code.

First I replaced stat.country as AnyObject !== "All" as AnyObject by stat.country != "All".

Then your filter in the ForEach was strange. I made it simpler and improved it using .contains instead of string equality. It enables to search a country without typing the whole name.

Here is a working body for your View :

var body: some View {
        NavigationView {
            VStack {
                SearchBar(text: $searchText)
                List {
                    ForEach(countryStats.filter { $0.country.contains(searchText) || searchText.isEmpty }) { stat in
                        if stat.country != "All" {

                            NavigationLink(destination: CountryDetailView(countryStats: stat)) {
                                HStack {
                                    URLImage(URL(string: stat.flag ?? "")!) { proxy in
                                        proxy.image
                                            .resizable()
                                            .scaledToFit()
                                            .clipped()

                                    }
                                    .frame(width: 50.0, height: 35.0)

                                    VStack(alignment: .leading) {
                                        Text(stat.country)
                                            .font(.headline)
                                        Text("Total cases: \(stat.cases)")
                                            .font(.subheadline)
                                    }
                                }

                            }
                        }
                    }
                }.navigationBarTitle("Cases By Country")
            }
        }
    }
Sign up to request clarification or add additional context in comments.

2 Comments

The reason I added stat.country as AnyObject !== "All" as AnyObject was because I get the error Cannot convert value of type 'String' to expected argument type 'AnyObject?'. Not sure why. But the search is working, thanks!
@GunasaiGarapati Maybe because you wrote !== instead of != ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.