Back to course overview
Module 4Data & persistence 14 min

Networking: calling an API from Swift

Learn async/await and URLSession — the foundation you'll need to call an AI model over the network in Module 5.

Your AI feature will call a model hosted on the internet. That means networking — sending an HTTP request and awaiting a response. Modern Swift makes this readable with async/await: code that waits for the network without freezing the UI.

The shape of every network call

QuoteService.swiftswift
import Foundation

struct Quote: Decodable {
    let quote: String
    let author: String
}

func fetchQuote() async throws -> Quote {
    let url = URL(string: "https://dummyjson.com/quotes/random")!
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    return try JSONDecoder().decode(Quote.self, from: data)
}

Read it top to bottom: build a URL, await the data (execution pauses here without blocking the screen), check the HTTP status, then decode the JSON into a Swift struct. Decodable means Swift maps JSON fields onto struct properties automatically — this API returns {"id": …, "quote": "…", "author": "…"}, and your struct declares only the fields it cares about; the decoder ignores the rest. Almost every API call you ever write is a variation of this.

Calling it from a view

swiftswift
struct QuoteView: View {
    @State private var quote: Quote?
    @State private var isLoading = false

    var body: some View {
        VStack {
            if let quote { Text(quote.quote) }
            Button("Get quote") {
                Task { await load() }   // Task bridges sync UI to async work
            }
        }
    }

    private func load() async {
        isLoading = true
        defer { isLoading = false }
        quote = try? await fetchQuote()
    }
}
Why the Task { } wrapper

Button actions are synchronous, but network calls are async. Task { await … } starts an asynchronous unit of work from synchronous code. You'll use this exact bridge every time a tap triggers a network request.

Three things that trip people up

1) Forgetting try/await — the compiler will remind you. 2) A Decodable struct whose field names don't match the JSON — decoding throws. 3) Doing UI updates off the main thread — in modern SwiftUI, keep state changes in the view's context and you're fine.