TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Add search to existing JSON list

Forums > SwiftUI

Hello,

I have some code, which retrieves JSON data from a URL and shows it in a List. This is working so far. What I try to do is to add a search function. I've tried to follow Paul's tutorial https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-a-search-bar-to-filter-your-data without success. I can't figure out how to implement that in my code.

Here is my code:

Part 1:

import Foundation
import SwiftUI

let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)

//API Class
class apiCall {

    func getUsers(completion:@escaping ([machines]) -> ()) {
        guard let url = URL(string: "http://192.168.7.1/horizon.json") else {return}
        URLSession.shared.dataTask(with: url) { (data, _, _) in

            let users = try! JSONDecoder().decode([machines].self, from: data!)
            ``//print(users)

            DispatchQueue.main.async {
                completion(users)
            }
        }
        .resume()
    }
}

struct machines: Codable, Identifiable {
    let id = UUID()
    let MACHINE_NR: String
    let MACHINE_COSTCENTER: String
    let MACHINE_AREA: String
    let ACTUAL_MACHINE_STATUS: Int

}

Part 2:

import SwiftUI

struct ContentView: View {

    @State var machines: [machines] = []
    @State private var showingAlert = false
    @State private var searchText = ""

    var body: some View {
        NavigationView{

            List(machines) { machines in
                VStack(alignment: .leading){
                Text(machines.MACHINE_NR)
                        .font(.system(size: 30))
                        .bold()
                    .foregroundColor(.orange)
                Text("Cost Center: \(machines.MACHINE_COSTCENTER)")
                    .font(.subheadline)
                Text("Area: \(machines.MACHINE_AREA)")
                    .font(.subheadline)
                Text("Machine Status: \(machines.ACTUAL_MACHINE_STATUS)")
                    .font(.subheadline)
                }
                .padding(1)

            }
            .refreshable {
                apiCall().getUsers{ (machines) in
                    self.machines = machines
                }
            }
            .onAppear{
                apiCall().getUsers{ (machines) in
                    self.machines = machines
                }
            }
            .searchable(text: $searchText)
            .navigationTitle("Machine Overview")
        }

    }

}

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

Can someone please help me to implement the search function?

Thanks in advance Kay

2      

Step 1: Don't over complicate your issue. Your first task is to pull a bunch of data out of the internet as a JSON stream and turn it into an array of structs that you can use.

I trust you've done this. However, I think this line of code is very confusing!

// You're declaring a variable named machines. 
// It's an array that is made up of, is this right?.... more machines?
@State var machines: [machines] = []

Step 2: I'd suggest a new strategy here. Perhaps you should name your array allMachines. Or companyMachines. Pick a better name.

Step 3: Now that you have an array of allMachines, stop calling it JSON data. It's not JSON data anymore, it's an array of [Machine] objects. This array is available to you to use any way convenient to your application.

Step 4: List the machines in your user interface. But consider your two options. (1) If you have NO search terms, you want to show allMachines. (2) If you have a searchTerm, you want to show matchingMachines.

Step 5: Write the code to define matchingMachines.
This is simple array filtering. Look in allMachines array and filter it for matches. This is NOT JSON data anymore!

// Suggested Code
// Define a computed property in your struct!
// matchingMachines is an array of Machines
var matchingMachines: [Machine] {
    // Is the searchTerm empty?
    if searchTerm.isEmpty {
        return allMachines // return the array holding ALL your machines
    } else {
        // this is where you implement search code.  
        return allMachines.filter { $0.MACHINE_NR.contains( searchString ) // am only looking for matches in the machine's number.
        // However, you may want to include matches in IP address, cost center, or other parameters
}

Whenever your user changes the searchTerm, this computed property is recalculated.
If the matchingMachines array is recalculated, then the List view is redrawn. Instantly.

Step 6: Modify your user interface to display each machine in the matchingMachine array.

// matchingMachines hold all Machines, some Machines, or no machines.
List(matchingMachines) { match in
    // Step 7: Create a separate view to display a single Machine.
    MachineView( machine: match ) // Do this! Make this a separate view!
}
.searchable( text: $searchText, prompt: "find machine")
.navigationTitle("Machines")

Step 8: Please let us know how you implemented this!

3      

@Obelix

Thanks for your fast help. I've got it. It is working now. I still need to do Step 6. After it is done, I will post my final code.

Kay

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.