I currently have a list of recipes that I fetch from a local API upon screen load. This page also has a search field on it that I want to hook up to pull certain data from this API. This API call stores the results in the state of the view. My goal is, when somebody searches, to pull those records and update the state, and reloading the view with new data.
I have seen different ways of doing this with @binding and @state, however, it seems from the examples I've been looking at the binding is in a different struct within another file. In my iteration, however, I just have some functions that pull data from this API. I'd like to avoid using reloadData()
as I'd like to use @binding
and @state
wherever possible in my application.
struct RecipesView: View {
@State var recipes = [Recipe]()
@State var searchText = ""
var body: some View {
VStack {
HStack {
TextField("Search ...", text: $searchText)
.padding(7)
.padding(.horizontal, 25)
.background(Color(.systemGray6))
.cornerRadius(8)
.padding(.horizontal, 10)
}
VStack{
HStack {
Text("Categories")
.bold()
.multilineTextAlignment(.trailing)
.padding(10)
Spacer()
}
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(SPIRITS, id:\.self){ spirit in
Button("\(spirit)", action: {
queryBySpirit(spirit: spirit)
})
.frame(width: 80, height: 40, alignment: .center)
.background(Color("Tope"))
.foregroundColor(Color("Onyx"))
.cornerRadius(15)
.font(.subheadline.bold())
}
}.padding([.leading, .trailing])
}
}
ScrollView {
VStack {
ForEach(recipes) { recipe in
NavigationLink(destination: RecipeView(recipe: recipe)) {
RecipeCard(recipe: recipe)
}
}.padding([.trailing])
}
}
}
.onAppear(perform: loadData)
.navigationBarTitle("Recipes")
.listStyle(InsetGroupedListStyle())
Color.gray.ignoresSafeArea()
}
func loadData() {
AF.request("http://localhost:3000/recipes").responseJSON { response in
guard let data = response.data else { return }
if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
DispatchQueue.main.async {
self.recipes = response
}
return
}
}
}
func queryBySpirit(spirit: String) {
AF.request("http://localhost:3000/recipes?spirit=\(spirit)").responseJSON { response in
guard let data = response.data else { return }
if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
DispatchQueue.main.async {
self.recipes = response
}
return
}
}
}
}
How can I use @binding
within this file to take advantage of the hot reloading? Or, based on my iteration will I need to leverage reloadData()
instead?
Spefically, I'd like to avoid using reloadData()
as seen here: Swift CollectionView reload data after API call
I'd love to be able to leverage Changing @State variable does not update the View in SwiftUI
However I'm unsure of how to do that with what I have currently.