0
0
iOS Swiftmobile~20 mins

Async functions in iOS Swift - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Async Data Loader
This screen fetches a random joke from an online API asynchronously and displays it. It shows a loading spinner while waiting for the data.
Target UI
-------------------------
| Async Data Loader     |
-------------------------
|                       |
|   [Loading Spinner]    |
|                       |
|                       |
|                       |
|                       |
|                       |
-------------------------
Use Swift async/await to fetch data from https://official-joke-api.appspot.com/random_joke
Show a loading spinner while fetching
Display the joke's setup and punchline after loading
Handle errors by showing an error message
Use SwiftUI for UI components
Starter Code
iOS Swift
import SwiftUI

struct AsyncDataLoaderView: View {
    @State private var joke: String = ""
    @State private var isLoading = false
    @State private var errorMessage: String? = nil

    var body: some View {
        VStack {
            // TODO: Add loading spinner, joke text, and error message here
        }
        .padding()
        .task {
            // TODO: Call async function to fetch joke here
        }
    }

    // TODO: Add async function to fetch joke here
}

struct AsyncDataLoaderView_Previews: PreviewProvider {
    static var previews: some View {
        AsyncDataLoaderView()
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import SwiftUI

struct AsyncDataLoaderView: View {
    @State private var joke: String = ""
    @State private var isLoading = false
    @State private var errorMessage: String? = nil

    var body: some View {
        VStack(spacing: 20) {
            if isLoading {
                ProgressView("Loading...")
                    .progressViewStyle(CircularProgressViewStyle())
                    .accessibilityLabel("Loading joke")
            } else if let error = errorMessage {
                Text("Error: \(error)")
                    .foregroundColor(.red)
                    .multilineTextAlignment(.center)
                    .accessibilityLabel("Error message")
            } else if !joke.isEmpty {
                Text(joke)
                    .font(.title3)
                    .multilineTextAlignment(.center)
                    .accessibilityLabel("Joke")
            } else {
                Text("No joke loaded.")
                    .foregroundColor(.gray)
            }
        }
        .padding()
        .task {
            await fetchJoke()
        }
    }

    func fetchJoke() async {
        isLoading = true
        errorMessage = nil
        joke = ""

        guard let url = URL(string: "https://official-joke-api.appspot.com/random_joke") else {
            errorMessage = "Invalid URL"
            isLoading = false
            return
        }

        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            if let decoded = try? JSONDecoder().decode(Joke.self, from: data) {
                joke = "\(decoded.setup)\n\n\(decoded.punchline)"
            } else {
                errorMessage = "Failed to decode joke"
            }
        } catch {
            errorMessage = error.localizedDescription
        }

        isLoading = false
    }
}

struct Joke: Decodable {
    let setup: String
    let punchline: String
}

struct AsyncDataLoaderView_Previews: PreviewProvider {
    static var previews: some View {
        AsyncDataLoaderView()
    }
}

This solution uses SwiftUI's .task modifier to start fetching the joke asynchronously when the view appears. The fetchJoke() function uses Swift's async/await with URLSession.shared.data(from:) to get data from the joke API.

While loading, a ProgressView spinner is shown. If the fetch succeeds, the joke's setup and punchline are displayed. If an error occurs, an error message is shown in red.

Accessibility labels are added for screen readers. The UI updates reactively based on the state variables isLoading, joke, and errorMessage.

Final Result
Completed Screen
-------------------------
| Async Data Loader     |
-------------------------
|                       |
|   (spinner animates)  |
|       Loading...       |
|                       |
|                       |
|                       |
-------------------------

After loading:
-------------------------
| Async Data Loader     |
-------------------------
|                       |
| Why did the chicken   |
| cross the road?       |
|                       |
| To get to the other   |
| side!                 |
-------------------------
When the screen appears, a spinner shows while the joke loads.
After loading, the joke text replaces the spinner.
If the network fails, an error message appears in red.
Stretch Goal
Add a button to reload a new joke on demand.
💡 Hint
Add a Button below the joke text that calls fetchJoke() asynchronously when tapped.