Reputation: 135
Currently my SearchView
uses a simple filter on array names
and I need it to make it search trough my JSON file. I have a JSON file with this structure:
struct UcmData: Codable, Identifiable {
let id: Int
let building: [Building]
}
// MARK: - Building
struct Building: Codable, Identifiable {
let id: Int
let title, subtitle, info, image: String
let floor: [Floor]
}
// MARK: - Floor
struct Floor: Codable, Identifiable {
let id, number: Int
let title, subtitle, image: String
let cabinet: [Cabinet]?
}
// MARK: - Cabinet
struct Cabinet: Codable, Identifiable {
let id: Int
let number: String
let person: [Person]
}
// MARK: - Person
struct Person: Codable, Identifiable {
let id: Int
let name: String
}
SearchView:
struct SearchView: View {
let names = ["306 B", "doc. Ing. Michal Čerňanský, PhD."]
let ucmData = Bundle.main.decode(UcmData.self, from: "ucm_data.json")
@State private var searchText = ""
@State private var showCancelButton: Bool = false
var body: some View {
VStack {
HStack {
HStack {
Image(systemName: "magnifyingglass")
TextField("Zadajte text pre vyhľadávanie", text: $searchText, onEditingChanged: { isEditing in
self.showCancelButton = true
}, onCommit: {
print("onCommit")
}).foregroundColor(.primary)
Button(action: {
self.searchText = ""
}) {
Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
}
}
.padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
.foregroundColor(.secondary)
.background(Color(.secondarySystemBackground))
.cornerRadius(10.0)
if showCancelButton {
Button("Cancel") {
UIApplication.shared.endEditing(true)
self.searchText = ""
self.showCancelButton = false
}
.foregroundColor(Color(.systemBlue))
}
}
.padding(.horizontal)
.navigationBarHidden(showCancelButton)
List {
ForEach(self.names.filter{
self.searchText.isEmpty ? $0.localizedStandardContains("") :
$0.localizedStandardContains(self.searchText)
}, id: \.self) { name in
Text(name)
}
}
.navigationBarTitle(Text("Vyhľadávanie"))
.resignKeyboardOnDragGesture()
}
}
}
extension UIApplication {
func endEditing(_ force: Bool) {
self.windows
.filter{$0.isKeyWindow}
.first?
.endEditing(force)
}
}
struct ResignKeyboardOnDragGesture: ViewModifier {
var gesture = DragGesture().onChanged{_ in
UIApplication.shared.endEditing(true)
}
func body(content: Content) -> some View {
content.gesture(gesture)
}
}
extension View {
func resignKeyboardOnDragGesture() -> some View {
return modifier(ResignKeyboardOnDragGesture())
}
}
struct SearchView_Previews: PreviewProvider {
static var previews: some View {
Group {
SearchView()
.environment(\.colorScheme, .light)
SearchView()
.environment(\.colorScheme, .dark)
}
}
}
How can I make match the searchText
with Cabinet.number
or Person.name
and list matching items with their path in JSON file e.g. "building.title > floor.title > cabinet.number" or for person "building.title > floor.title > cabinet.number > person.name"? Thank you for suggestions.
Upvotes: 1
Views: 877
Reputation: 20274
Not entirely sure but I am assuming the search field can have either a person's name or a cabinet number based on which you want to filter your dataset and show a list in the fashion:
building.title > floor.title > cabinet.number
or
building.title > floor.title > cabinet.number > person.name
Maybe something along these lines might help:
struct ContentView: View {
let data: UcmData
@State var searchString = ""
var found: [String] {
var result = [String]()
data.building.forEach { (building) in //go through every building
building.floor.forEach { (floor) in //go through every floor
floor.cabinet?.forEach { (cabinet) in //go through every cabinet
var cabinetFound: String {
return "\(building.title) > \(floor.title) > \(cabinet.number)"
}
if cabinet.number == searchString { //check with cabinet [!]
result.append(cabinetFound) //add search result
} else {
cabinet.person.forEach { (person) in //go through every person
if person.name == searchString { //check with person [!]
let personFound = cabinetFound + " > \(person.name)"
result.append(personFound) //add search result
}
}
}
}
}
}
return result
}
var body: some View {
VStack {
TextField("search string", text: $searchString) //search field
List(found, id: \.self) { (current) in //List of search results
Text(current)
}
}
}
}
*This example is just a sub-set of your view to showcase only the search functionality. Integrate it if it makes sense.
found
is an Array
of String
that will be computed when requestedsearchString
updates and re-renders the body which will refresh the List
with elements in found
I hope this helps :)
Upvotes: 1