Search code examples
swiftuiuisearchbar

Using Searchbar to dynamically show data in SwiftUI from an Array


I need some help trying to work with .searchable in Swift UI.

I am trying to present a few pieces of data that are associated with the search string.

For example, the user searches for a certain description, and the code/price is also presented in the row. I do not want to present the data on a new view, just dynamically as the user is typing.

The Row with values

The problem I am having is that I cannot find a way to manipulate the computed value "search results" and also the generic "named: in the ForEach loop.

I understand the "search results" is an array but I can't a way to access it.

The removalAll was added because every time I went back to this view, it duplicated the list every time a character was typed in the search bar.

I have the data as an enum, and stored in an array because at the time it made sense in case I needed to iterate over the data.

Any advice or suggestions to do this?

Thank you in advance!

Here is the main view:

struct ContentView: View {

        @State private var searchText = ""
    
        var body: some View {
            
        storedOtherWoundsNames.removeAll()
            
        self.createOtherWound()
        return
         NavigationView {
                List {
                    ForEach(searchResults, id: \.self) { named in
                        VStack {
                            VStack {
                                Text(named)  //search result
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .font(.caption)
                            }
                            HStack {
                                Text("\(named) Code")  //want to display from the array "storedOtherWoundsCodes"
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .font(.caption)
                                Text("\(named) Value")  //want to display from the array "storedOtherWoundsValues"
                                    .frame(maxWidth: .infinity, alignment: .trailing)
                            }
                        }
                              
                          .padding(.all)
                          .border(Color.gray)
                        }
                    }
                }
                .searchable(text: $searchText)
            }

        var searchResults: [String] {
            if searchText.isEmpty {
                storedOtherWoundsNames.removeAll()
                return storedOtherWoundsNames
            } else {
             
                return storedOtherWoundsNames.filter{$0.contains(searchText)
            }
        }
           
    }
        
    func createOtherWound() {  //takes enum and places it in array
        for otherWoundName in allOtherWoundsNames {
            storedOtherWoundsNames.append(otherWoundName.rawValue)
        }
        for otherWoundCode in allOtherWoundsCodes {
            storedOtherWoundsCodes.append(otherWoundCode.rawValue)
        }
        for otherWoundValue in allOtherWoundsValues {
            storedOtherWoundsValues.append(otherWoundValue.rawValue)
        }
    }
}
    


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Here is the data if it helps:


    enum OtherWoundsName: String, CaseIterable {
    case secondaryclosure_name = "Secondary Closure"
    case musclerepair_name = "Muscle Repair (including skin)"
    case woundone_name = "Debride Wound/Ulcer - One"
    case woundtwo_name = "Debride Wound/Ulcer - Two"
    case woundthree_name = "Debride Wound/Ulcer - Three"
    case woundfourmore_name = "Debride Wound/Ulcer - Four Plus"
    case debrideone_name = "Debride into Tendon/Bone/Bursa/Bone"
    case debridetwo_name = "Debride into Tendon/Bone/Bursa/Bone - Two or More"
    case extensortendon_name = "Extensor Tendon Repair"
    case flexortendor_name = "Flexor Tendon Repair"
    case fbskin_name = "FB Skin"
    case fbskinsedation_name = "FB Skin with Sedation"
    case fbdermissedation_name = "FB Dermis with Sedation"
    case flapface5_name = "Advancement Flap: Neck Up < 5.1cm"
    case flapother5_name = "Advancement Flap: Other < 5.1cm"
    case flapface5plus_name = "Advancement Flap: Neck up > 10.1cm"
    case flapother5plus_name = "Advancement Flap: Other > 10.1.cm"
    case fingerdebride_name = "Finger Debridement"
    case handdebride_name = "Hand Debridement"
    case facedebride_name = "Face Debridement"
    case otherdebride_name = "Other per % BSA"
    case ampulatephalanx_name = "Amputate Phalanx"
    case revisionfingertip_name = "Revision of Amputation Finger Tip"
}
enum OtherWoundsCode: String, CaseIterable {
    case secondaryclosure_code = "Z738"
    case musclerepair_code = "R525"
    case woundone_code = "Z080"
    case woundtwo_code = "Z081"
    case woundthree_code = "Z082"
    case woundfourmore_code = "Z083"
    case debrideone_code = "Z084"
    case debridetwo_code = "Z085"
    case extensortendon_code = "R578"
    case flexortendor_code = "R585"
    case fbskin_code = "Z114"
    case fbskinsedation_code = "Z115"
    case fbdermissedation_code = "R517"
    case flapface5_code = "R011"
    case flapother5_code = "R002"
    case flapface5plus_code = "R012"
    case flapother5plus_code = "R003"
    case fingerdebride_code = "R660"
    case handdebride_code = "R661"
    case facedebride_code = "R662"
    case otherdebride_code = "R637"
    case ampulatephalanx_code = "R606"
    case revisionfingertip_code = "R629"
}
enum OtherWoundsValue: Double, CaseIterable {
    case secondaryclosure_value = 97.35
    case musclerepair_value = 88.60
    case woundone_value = 20
    case woundtwo_value = 30
    case woundthree_value = 45
    case woundfourmore_value = 60
    case debrideone_value = 60.001
    case debridetwo_value = 90
    case extensortendon_value = 164.10
    case flexortendor_value = 307.60
    case fbskin_value = 25.20
    case fbskinsedation_value = 88.80
    case fbdermissedation_value = 107.70
    case flapface5_value = 89.85
    case flapother5_value = 67.40
    case flapface5plus_value = 247.15
    case flapother5plus_value = 161.75
    case fingerdebride_value = 28.90
    case handdebride_value = 47.85
    case facedebride_value = 28.901
    case otherdebride_value = 29.68
    case ampulatephalanx_value = 161.45
    case revisionfingertip_value = 241.55
}

let allOtherWoundsNames = OtherWoundsName.allCases
var storedOtherWoundsNames: [String] = []

let allOtherWoundsCodes = OtherWoundsCode.allCases
var storedOtherWoundsCodes: [String] = []

let allOtherWoundsValues = OtherWoundsValue.allCases
var storedOtherWoundsValues: [Double] = []

Solution

  • Try this approach as shown in the example code, where the data is in an array of structs ([DataModel]), and the search returns a filtered array that can be used to "access" the name, code and value of each item.

    struct ContentView: View {
        @State private var searchText = ""
        @State var items = [DataModel]()
        
        var searchResults: [DataModel] {
            searchText.isEmpty ? [] : items.filter{$0.name.contains(searchText)}
            // or alternatively
            // searchText.isEmpty ? items : items.filter{$0.name.contains(searchText)}
        }
        
        var body: some View {
            NavigationView {
                List {
                    ForEach(searchResults) { item in
                        VStack {
                            Text(item.name).font(.caption)
                            HStack {
                                Text(item.code).font(.caption).foregroundColor(.red)
                                Text("\(item.value)").foregroundColor(.blue)
                            }
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding(.all)
                        .border(Color.gray)
                    }
                }
            }
            .searchable(text: $searchText)
            .onAppear {
               items = [DataModel(name: "Secondary Closure", code: "Z738", value: 97.35),
                        DataModel(name: "Muscle Repair (including skin)", code: "R525", value: 88.60),
                        DataModel(name: "Debride Wound/Ulcer - One", code: "Z080", value: 20)]
                   // etc...
            }
        }
        
    }
    
    struct DataModel: Identifiable {
        let id = UUID()
        var name: String
        var code: String
        var value: Double
    }