Attempting to display text to a view once my API call is complete. When a user submits there textfield, in onSubmit
, I save the variable to my Binding foodName, which then gets passed to the new View (searchResultsView) If I display the data as Text, it works, but once I attempt to pass the data and display it in a separate view it does not.
My guess is I'm loading the view before the api call (or I could be completely off with my guess which I probably am). Any feedback would be helpful. Thank you.
API CALL
class FoodApiSearch: ObservableObject{
@Published var foodDescription = ""
//will search for user Input
func searchFood(userItem: String){
//calls api search
guard let url = URL(string: "https://api.nal.usda.gov/fdc/v1/foods/search?query=\(userItem)&dataType=&pageSize=1&pageNumber=1&api_key=***tDvDZVOy8cqG") else {return}
URLSession.shared.dataTask(with: url) { (data, _,_) in
let searchResults = try! JSONDecoder().decode(APISearchResults.self, from: data!)
DispatchQueue.main.async {
for item in searchResults.foods{
self.foodDescription = item.foodDescription ?? "food not valid"
print(self.foodDescription)
}
}
}
.resume()
}
}
Struct that calls SearchResultsView
struct testA: View {
//Textfield
@State var userFoodInput = ""
@State private var didUserSearch = false
//will store api var of foodName
@StateObject private var foodApi = FoodApiSearch()
//send to food results view
@State private var foodName = ""
var body: some View {
VStack{
TextField("enter first name", text: $userFoodInput)
.onSubmit {
didUserSearch = true
foodApi.searchFood(userItem: userFoodInput)
foodName = foodApi.foodDescription
}
FoodSearchResultsView(foodName: $foodName, userSearch: $didUserSearch)
}
}
}
SearchResultsView (view being called)
struct FoodSearchResultsView: View {
//calls API
@StateObject private var foodApi = FoodApiSearch()
@State private var searchResultsItem = ""
@Binding var foodName: String
//if toggled, will display, binded to search bar
@Binding var userSearch: Bool
var body: some View {
if userSearch{
VStack{
Text("Best Match")
HStack{
VStack(alignment: .leading){
Text(foodName)
Text("1 Cup")
.font(.caption)
.offset(y:8)
}
.foregroundColor(.black)
Spacer()
Text("72 Cals")
}
.frame(width:225, height:50)
.padding([.leading, .trailing], 45)
.padding([.top, .bottom], 10)
.background(RoundedRectangle(
cornerRadius:20).fill(Color("LightWhite")))
.foregroundColor(.black)
Button("Add Food"){
userSearch = false
}
.padding()
}
.frame(maxWidth:.infinity, maxHeight: 700)
}
}
}
CodePudding user response:
Your suspicion was about right:
when you do:
foodName = foodApi.foodDescription
the network call did not finish yet.
Restructure your code. there is no need for the foodName
variable as you have allready one in your FoodApiSearch
. Pass that on to your subview and access it there. As soon as the network call completes it should update your view and show the result.
struct testA: View {
//Textfield
@State var userFoodInput = ""
@State private var didUserSearch = false
//will store api var of foodName
@StateObject private var foodApi = FoodApiSearch()
//send to food results view
//@State private var foodName = ""
var body: some View {
VStack{
TextField("enter first name", text: $userFoodInput)
.onSubmit {
didUserSearch = true
foodApi.searchFood(userItem: userFoodInput)
// foodName = foodApi.foodDescription
}
FoodSearchResultsView(userSearch: $didUserSearch)
.environmentObject(foodApi)
}
}
}
struct FoodSearchResultsView: View {
//calls API
@EnvironmentObject private var foodApi: FoodApiSearch
@State private var searchResultsItem = ""
// @Binding var foodName: String
//if toggled, will display, binded to search bar
@Binding var userSearch: Bool
var body: some View {
if userSearch{
VStack{
Text("Best Match")
HStack{
VStack(alignment: .leading){
Text(foodApi.foodDescription)
Text("1 Cup")
.font(.caption)
.offset(y:8)
}
.foregroundColor(.black)
Spacer()
Text("72 Cals")
}
.frame(width:225, height:50)
.padding([.leading, .trailing], 45)
.padding([.top, .bottom], 10)
.background(RoundedRectangle(
cornerRadius:20).fill(Color("LightWhite")))
.foregroundColor(.black)
Button("Add Food"){
userSearch = false
}
.padding()
}
.frame(maxWidth:.infinity, maxHeight: 700)
}
}
}