Attempting to display text to a view once my API call is complete. I figured I would just save the API result I wanted (foodDescription) to a variable once the data loaded, then display the variable in the view.
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). I also feel I am using the DispatchQueue incorrectly and would love to feedback as well. Thank you in advance.
API CALL
class FoodApiSearch{
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=*****") 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
struct testA: View {
//Textfield
@State var userFoodInput = ""
//will store api var of foodName
@State var foodName = ""
var body: some View {
VStack{
TextField("enter first name", text: $userFoodInput)
Button("click me"){
//action
FoodApiSearch().searchFood(userItem: userFoodInput)
foodName = FoodApiSearch().foodDescription
}
Text("You searched for " foodName)
}
}
}
CodePudding user response:
Because searchFood
is asynchronous, you can't just assign foodName = FoodApiSearch().foodDescription
on a subsequent line and expect it to return the right result. There's also another issue: you're creating two different instances of FoodApiSearch
(each time you type FoodApiSearch()
it's a new instance).
You're probably looking to make FoodApiSearch
an ObservableObject
with a @Published
property. That would look like this:
class FoodApiSearch : ObservableObject {
@Published var foodDescription = ""
func searchFood(userItem: String) {
Then, you store a single instance of it using @StateObject
in your View
and if you want to display the result, use the @Published
variable -- don't try to assign it to a local @State
:
struct TestA: View {
@State private var userFoodInput = ""
@StateObject private var foodApi = FoodApiSearch()
var body: some View {
VStack{
TextField("enter first name", text: $userFoodInput)
Button("click me") {
foodApi.searchFood(userItem: userFoodInput)
}
if !foodApi.foodDescription.isEmpty {
Text(foodApi.foodDescription)
}
}
}
}