发出获取请求时出错以及如何在接收到数据后显示数据
Error When Making Get Request & How To Display Data Once Received
发出获取请求时出现以下错误:
Thread 3: Fatal error: Unexpectedly found nil while unwrapping an Optional value
数据模型+获取请求模型如下:
struct Get: Codable, Identifiable {
var id = UUID()
var meals: [String: String?]
}
class Api {
func getData(completion: @escaping ([Get]) -> ()) {
guard let url = URL(string: "www.themealdb.com/api/json/v1/1/random.php") else {return}
URLSession.shared.dataTask(with: url) { (data, _, _) in
let get = try! JSONDecoder().decode([Get].self, from: data!)
DispatchQueue.main.async {
completion(get)
}
}
.resume()
}
}
示例视图:
struct apitestview: View {
@State var getRecipe: [Get] = []
var body: some View {
List(getRecipe) { get in
Text("placeholder") //once the data is received, how will I display the data here?
}
.onAppear() {
Api().getData { (get) in
self.getRecipe = get
}
}
}
}
此外,接收到数据后如何在视图中显示它?嵌套的 JSON 结构让我很困惑。我对收到请求很陌生。它不需要显示在列表中,它可以是一个巨大的 Text()
元素。
下面是示例 JSON 对象:
{
"meals": [
{
"idMeal": "52858",
"strMeal": "New York cheesecake",
"strDrinkAlternate": null,
"strCategory": "Dessert",
"strArea": "American",
"strInstructions": "Position an oven shelf in the middle of the oven. Preheat the oven to fan 160C/conventional 180C/gas 4. Line the base of a 23cm springform cake tin with parchment paper. For the crust, melt the butter in a medium pan. Stir in the biscuit crumbs and sugar so the mixture is evenly moistened. Press the mixture into the bottom of the pan and bake for 10 minutes. Cool on a wire rack while preparing the filling.\r\nFor the filling, increase the oven temperature to fan 200C/conventional 240C/gas 9. In a table top mixer fitted with the paddle attachment, beat the soft cheese at medium-low speed until creamy, about 2 minutes. With the mixer on low, gradually add the sugar, then the flour and a pinch of salt, scraping down the sides of the bowl and the paddle twice.\r\nSwap the paddle attachment for the whisk. Continue by adding the vanilla, lemon zest and juice. Whisk in the eggs and yolk, one at a time, scraping the bowl and whisk at least twice. Stir the 284ml carton of soured cream until smooth, then measure 200ml/7fl oz (just over 3⁄4 of the carton). Continue on low speed as you add the measured soured cream (reserve the rest). Whisk to blend, but don't over-beat. The batter should be smooth, light and somewhat airy.\r\nBrush the sides of the springform tin with melted butter and put on a baking sheet. Pour in the filling - if there are any lumps, sink them using a knife - the top should be as smooth as possible. Bake for 10 minutes. Reduce oven temperature to fan 90C/conventional 110C/gas 1⁄4 and bake for 25 minutes more. If you gently shake the tin, the filling should have a slight wobble. Turn off the oven and open the oven door for a cheesecake that's creamy in the centre, or leave it closed if you prefer a drier texture. Let cool in the oven for 2 hours. The cheesecake may get a slight crack on top as it cools.\r\nCombine the reserved soured cream with the 142ml carton, the sugar and lemon juice for the topping. Spread over the cheesecake right to the edges. Cover loosely with foil and refrigerate for at least 8 hours or overnight.\r\nRun a round-bladed knife around the sides of the tin to loosen any stuck edges. Unlock the side, slide the cheesecake off the bottom of the tin onto a plate, then slide the parchment paper out from underneath.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/swttys1511385853.jpg",
"strTags": "Desert,Dairy,Pudding,Cake,Breakfast",
"strYoutube": "https://www.youtube.com/watch?v=tspdJ6hxqnc",
"strIngredient1": "Butter",
"strIngredient2": "Sour Cream",
"strIngredient3": "Sugar",
"strIngredient4": "Cream Cheese",
"strIngredient5": "Caster Sugar",
"strIngredient6": "Plain Flour",
"strIngredient7": "Lemon Juice",
"strIngredient8": "Eggs",
"strIngredient9": "Sour Cream",
"strIngredient10": "Sour Cream",
"strIngredient11": "Caster Sugar",
"strIngredient12": "Lemon Juice",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "85g",
"strMeasure2": "140g",
"strMeasure3": "1tbsp",
"strMeasure4": "900g",
"strMeasure5": "250g",
"strMeasure6": "3 tbs",
"strMeasure7": "1 ½ teaspoons",
"strMeasure8": "3 Large",
"strMeasure9": "250ml",
"strMeasure10": "150ml",
"strMeasure11": "1 tbsp",
"strMeasure12": "2 tsp",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "https://www.bbcgoodfood.com/recipes/2869/new-york-cheesecake",
"strImageSource": null,
"strCreativeCommonsConfirmed": null,
"dateModified": null
}
]
}
不久前我确实问过一个类似的问题,这帮助我在这个问题上指明了正确的方向。我仍然无法弄清楚这一点。有什么想法吗?
附加信息:
抛出 nil 引用的图像:
编辑 2 - 发布新版本的代码:
class Api {
func getData(completion: @escaping (Get) -> ()) {
guard let url = URL(string: "www.themealdb.com/api/json/v1/1/random.php") else {return}
URLSession.shared.dataTask(with: url) { (data, _, _) in
let get = try! JSONDecoder().decode(Get.self, from: data!)
DispatchQueue.main.async {
completion(get)
}
}
.resume()
}
}
struct apitestview: View {
@State var getRecipe: Get
var body: some View {
//List(getRecipe) { get in
Text("placeholder")
//}
.onAppear() {
Api().getData { (get) in
self.getRecipe = get
}
}
}
}
这里有一个示例代码可以帮助您解决问题。您可以轻松地将其重构为您的“Api”class 和“获取”内容。
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct MealResponse: Codable {
var meals: [[String: String?]]
}
struct ContentView: View {
@State var meals = [[String: String?]]()
@State var firstMeal = [String: String?]()
var body: some View {
List {
ForEach(firstMeal.keys.sorted(), id: \.self) { key in
VStack {
Text(key)
Text((firstMeal[key] ?? "") ?? "")
}
}
}
// if using Swift 5.5 ios-15
// .task {
// let response: MealResponse? = await fetchMeals()
// if let resp = response, let firstOne = resp.meals.first {
// meals = resp.meals
// firstMeal = firstOne
// }
// }
.onAppear { loadData() }
}
func loadData() {
guard let url = URL(string: "https://www.themealdb.com/api/json/v1/1/random.php") else {
print("Your API end point is Invalid")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(MealResponse.self, from: data) {
DispatchQueue.main.async {
if let firstOne = response.meals.first {
meals = response.meals
firstMeal = firstOne
}
}
return
}
}
}.resume()
}
// if using Swift 5.5 ios-15
func fetchMeals<T: Decodable>() async -> T? {
let url = URL(string: "https://www.themealdb.com/api/json/v1/1/random.php")!
let request = URLRequest(url: url)
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
// throw URLError(.badServerResponse) // todo
print(URLError(.badServerResponse))
return nil
}
let results = try JSONDecoder().decode(T.self, from: data)
return results
}
catch {
return nil
}
}
}
发出获取请求时出现以下错误:
Thread 3: Fatal error: Unexpectedly found nil while unwrapping an Optional value
数据模型+获取请求模型如下:
struct Get: Codable, Identifiable {
var id = UUID()
var meals: [String: String?]
}
class Api {
func getData(completion: @escaping ([Get]) -> ()) {
guard let url = URL(string: "www.themealdb.com/api/json/v1/1/random.php") else {return}
URLSession.shared.dataTask(with: url) { (data, _, _) in
let get = try! JSONDecoder().decode([Get].self, from: data!)
DispatchQueue.main.async {
completion(get)
}
}
.resume()
}
}
示例视图:
struct apitestview: View {
@State var getRecipe: [Get] = []
var body: some View {
List(getRecipe) { get in
Text("placeholder") //once the data is received, how will I display the data here?
}
.onAppear() {
Api().getData { (get) in
self.getRecipe = get
}
}
}
}
此外,接收到数据后如何在视图中显示它?嵌套的 JSON 结构让我很困惑。我对收到请求很陌生。它不需要显示在列表中,它可以是一个巨大的 Text()
元素。
下面是示例 JSON 对象:
{
"meals": [
{
"idMeal": "52858",
"strMeal": "New York cheesecake",
"strDrinkAlternate": null,
"strCategory": "Dessert",
"strArea": "American",
"strInstructions": "Position an oven shelf in the middle of the oven. Preheat the oven to fan 160C/conventional 180C/gas 4. Line the base of a 23cm springform cake tin with parchment paper. For the crust, melt the butter in a medium pan. Stir in the biscuit crumbs and sugar so the mixture is evenly moistened. Press the mixture into the bottom of the pan and bake for 10 minutes. Cool on a wire rack while preparing the filling.\r\nFor the filling, increase the oven temperature to fan 200C/conventional 240C/gas 9. In a table top mixer fitted with the paddle attachment, beat the soft cheese at medium-low speed until creamy, about 2 minutes. With the mixer on low, gradually add the sugar, then the flour and a pinch of salt, scraping down the sides of the bowl and the paddle twice.\r\nSwap the paddle attachment for the whisk. Continue by adding the vanilla, lemon zest and juice. Whisk in the eggs and yolk, one at a time, scraping the bowl and whisk at least twice. Stir the 284ml carton of soured cream until smooth, then measure 200ml/7fl oz (just over 3⁄4 of the carton). Continue on low speed as you add the measured soured cream (reserve the rest). Whisk to blend, but don't over-beat. The batter should be smooth, light and somewhat airy.\r\nBrush the sides of the springform tin with melted butter and put on a baking sheet. Pour in the filling - if there are any lumps, sink them using a knife - the top should be as smooth as possible. Bake for 10 minutes. Reduce oven temperature to fan 90C/conventional 110C/gas 1⁄4 and bake for 25 minutes more. If you gently shake the tin, the filling should have a slight wobble. Turn off the oven and open the oven door for a cheesecake that's creamy in the centre, or leave it closed if you prefer a drier texture. Let cool in the oven for 2 hours. The cheesecake may get a slight crack on top as it cools.\r\nCombine the reserved soured cream with the 142ml carton, the sugar and lemon juice for the topping. Spread over the cheesecake right to the edges. Cover loosely with foil and refrigerate for at least 8 hours or overnight.\r\nRun a round-bladed knife around the sides of the tin to loosen any stuck edges. Unlock the side, slide the cheesecake off the bottom of the tin onto a plate, then slide the parchment paper out from underneath.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/swttys1511385853.jpg",
"strTags": "Desert,Dairy,Pudding,Cake,Breakfast",
"strYoutube": "https://www.youtube.com/watch?v=tspdJ6hxqnc",
"strIngredient1": "Butter",
"strIngredient2": "Sour Cream",
"strIngredient3": "Sugar",
"strIngredient4": "Cream Cheese",
"strIngredient5": "Caster Sugar",
"strIngredient6": "Plain Flour",
"strIngredient7": "Lemon Juice",
"strIngredient8": "Eggs",
"strIngredient9": "Sour Cream",
"strIngredient10": "Sour Cream",
"strIngredient11": "Caster Sugar",
"strIngredient12": "Lemon Juice",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "85g",
"strMeasure2": "140g",
"strMeasure3": "1tbsp",
"strMeasure4": "900g",
"strMeasure5": "250g",
"strMeasure6": "3 tbs",
"strMeasure7": "1 ½ teaspoons",
"strMeasure8": "3 Large",
"strMeasure9": "250ml",
"strMeasure10": "150ml",
"strMeasure11": "1 tbsp",
"strMeasure12": "2 tsp",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "https://www.bbcgoodfood.com/recipes/2869/new-york-cheesecake",
"strImageSource": null,
"strCreativeCommonsConfirmed": null,
"dateModified": null
}
]
}
不久前我确实问过一个类似的问题,这帮助我在这个问题上指明了正确的方向。我仍然无法弄清楚这一点。有什么想法吗?
附加信息:
抛出 nil 引用的图像:
编辑 2 - 发布新版本的代码:
class Api {
func getData(completion: @escaping (Get) -> ()) {
guard let url = URL(string: "www.themealdb.com/api/json/v1/1/random.php") else {return}
URLSession.shared.dataTask(with: url) { (data, _, _) in
let get = try! JSONDecoder().decode(Get.self, from: data!)
DispatchQueue.main.async {
completion(get)
}
}
.resume()
}
}
struct apitestview: View {
@State var getRecipe: Get
var body: some View {
//List(getRecipe) { get in
Text("placeholder")
//}
.onAppear() {
Api().getData { (get) in
self.getRecipe = get
}
}
}
}
这里有一个示例代码可以帮助您解决问题。您可以轻松地将其重构为您的“Api”class 和“获取”内容。
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct MealResponse: Codable {
var meals: [[String: String?]]
}
struct ContentView: View {
@State var meals = [[String: String?]]()
@State var firstMeal = [String: String?]()
var body: some View {
List {
ForEach(firstMeal.keys.sorted(), id: \.self) { key in
VStack {
Text(key)
Text((firstMeal[key] ?? "") ?? "")
}
}
}
// if using Swift 5.5 ios-15
// .task {
// let response: MealResponse? = await fetchMeals()
// if let resp = response, let firstOne = resp.meals.first {
// meals = resp.meals
// firstMeal = firstOne
// }
// }
.onAppear { loadData() }
}
func loadData() {
guard let url = URL(string: "https://www.themealdb.com/api/json/v1/1/random.php") else {
print("Your API end point is Invalid")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(MealResponse.self, from: data) {
DispatchQueue.main.async {
if let firstOne = response.meals.first {
meals = response.meals
firstMeal = firstOne
}
}
return
}
}
}.resume()
}
// if using Swift 5.5 ios-15
func fetchMeals<T: Decodable>() async -> T? {
let url = URL(string: "https://www.themealdb.com/api/json/v1/1/random.php")!
let request = URLRequest(url: url)
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
// throw URLError(.badServerResponse) // todo
print(URLError(.badServerResponse))
return nil
}
let results = try JSONDecoder().decode(T.self, from: data)
return results
}
catch {
return nil
}
}
}