Exercise 2: Fetching and Displaying a Joke with async/await and .task — Possible Solution ================================================================================================ struct Joke: Decodable { let setup: String let punchline: String } func fetchJoke() async throws -> Joke { let url = URL(string: "https://api.example.com/joke")! let (data, response) = try await URLSession.shared.data(from: url) guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw URLError(.badServerResponse) } return try JSONDecoder().decode(Joke.self, from: data) } struct JokeView: View { @State private var joke: Joke? @State private var errorMessage: String? var body: some View { Group { if let joke { VStack(alignment: .leading, spacing: 8) { Text(joke.setup) Text(joke.punchline).font(.headline) } } else if let errorMessage { Text("Error: \(errorMessage)") } else { ProgressView() } } .task { do { joke = try await fetchJoke() } catch { errorMessage = error.localizedDescription } } } } HOW IT WORKS: fetchJoke() follows the chapter's own fetchQuote() pattern exactly - marked async throws, using URLSession.shared.data(from:) with try await, checking for a real 200 status code, and decoding the response Data into a real Joke value via JSONDecoder().decode(Joke.self, from: data). Joke conforms to Decodable, so its two properties (setup and punchline) are matched automatically against the JSON response's own matching keys. JokeView holds two optional @State properties - joke (nil until loaded) and errorMessage (nil unless something goes wrong) - and its body branches on which one is currently set, showing a ProgressView while both remain nil. The .task {} modifier runs its own async closure once the view appears, calling fetchJoke() inside a do/catch block - a successful result sets joke, while any thrown error is caught and its description stored in errorMessage instead of crashing or silently failing. ANSWER: A Joke struct conforming to Decodable, an async throws fetchJoke() function following the chapter's own URLSession/JSONDecoder pattern, and a JokeView using .task with do/catch to load and display either the joke, an error message, or a loading indicator, correctly implements the requested networking flow end to end. WHY THIS WORKS AS AN ANSWER ------------------------------ This correctly mirrors the chapter's own fetchQuote()/.task pattern for a new Decodable type, including proper error handling via do/catch rather than omitting it.