I am having issues using searchable in SwiftUI for my data model. I have read the data from firestore and saved as an array in GBplayerModel().
When using searchable it filters the list of players but won't go back to the original list view when the search text is removed.
Essentially I want to main view be the full list that can be searched through.
Data Model
import Foundation
import Firebase
struct GBplayer: Identifiable {
var id: String
var name: String
var status: String
var height: String
var team: String
var position: String
}
// mock data that we can use for previews
extension GBplayer {
static func mock() -> GBplayer {
GBplayer(id: "Aaron Menzies", name: "Aaron Menzies", status: "Active", height: "7ft 3", team: "Leicester Riders", position: "C")
}
}
class GBplayerModel: ObservableObject {
@Published var playerList = [GBplayer]()
func getGBplayers() {
// get reference to the db
let db = Firestore.firestore()
db.collection("GBplayers").getDocuments { snapshot, error in
//check for errors
if error == nil {
// no errors
print("your in")
if let snapshot = snapshot {
// Update the list property in the main thread
DispatchQueue.main.async {
// get all the documents and create list of players
self.playerList = snapshot.documents.map { d in
//create a player struc for each player in the document
return GBplayer(id: d.documentID,
name: d["name"] as? String ?? "",
status: d["status"] as? String ?? "",
height: d["height"] as? String ?? "",
team: d["team"] as? String ?? "",
position: d["position"] as? String ?? "",
}
}
}
} else {
// handle the error
print("No Documents")
return
}
}
}
}
View
import SwiftUI
struct playerView: View {
@ObservedObject var model = GBplayerModel()
@State private var searchPlayer = ""
var body: some View {
NavigationView {
List {
ForEach (model.playerList) { player in
NavigationLink {
Text("Detailed View") //need to add detailed view here
} label: {
PlayerRow(player: player)
}
}
}
.listStyle(.plain)
.navigationTitle("Players")
// update data from Firebase
.onAppear() {
self.model.getGBplayers()
}
.searchable(text: $searchPlayer, placement: .navigationBarDrawer(displayMode: .always), prompt: "Find a player")
.onChange(of: searchPlayer) { search in
if !search.isEmpty {model.playerList = model.playerList.filter { $0.name.contains(searchPlayer) }
} else {
model.playerList = model.playerList
}
}
}
}
}
Any help would be greatly appreciated, spent a lot time of this and appreciate it maybe something simple
Right now, when you search, you're mutating your playerList
when you do this:
model.playerList = model.playerList.filter { ... }
That means that if you change your search term (or get rid of it), the items that were filtered out no longer exist.
Instead, you should always keep the full playerList
and only display a filtered version:
var filteredList: [GBplayer] {
if search.isEmpty {
return model.playerList
} else {
return model.playerList.filter { ... }
}
}
// ...
ForEach (filteredList) { player in ... }
And remove your onChange
code