Intro to iOS Development · SP26 · Prereading

Networking I

HTTP requests, JSON, async/await, and Codable
TL;DR — Most apps talk to a server. They do this over HTTP, sending requests and getting back JSON. In Swift, we use URLSession with async/await to make these calls without freezing the UI, and the Codable protocol to convert JSON into Swift structs.

Clients & Servers

A client is anything that asks for data—your iPhone, a laptop, a browser. A server is what answers. When you open Instagram, your phone (client) asks Instagram’s server for your feed, and the server sends back the posts.

Apps that rely on a server include social media, multiplayer games, and campus apps like Eatery. Apps that don’t need one—like the Camera app or Notes—work entirely on-device.

Key Takeaway

If your app shows data that lives somewhere else (another user's post, a menu, a leaderboard), it needs networking.

HTTP Requests

HTTP (HyperText Transfer Protocol) is the language clients and servers use to talk. A client sends a request, and the server sends back a response with a status code.

Request Methods

The four most common methods are GET (read data), POST (create data), PUT (update data), and DELETE (remove data). In this lecture we’ll focus on GET and POST.

Status Codes

Every HTTP response comes with a numeric status code. Here’s what each range means:

CodeCategoryWhat it means
1XXInfoRequest received, still processing
2XXSuccessEverything worked (200 OK is the most common)
3XXRedirectResource moved somewhere else
4XXClient ErrorSomething wrong with your request (404 = not found)
5XXServer ErrorServer broke trying to handle a valid request

JSON

JSON (JavaScript Object Notation) is the standard format for sending data over HTTP. It’s just key-value pairs—similar to a Swift dictionary. Keys are always strings; values can be strings, numbers, booleans, arrays, objects, or null.

{
  "age": 19,
  "classes": ["CS 1998", "PHYS 2213"],
  "major": "Info Sci",
  "name": "Vin Bui"
}

Notice how this maps almost directly to a Swift struct with properties age: Int, classes: [String], major: String, and name: String. That’s the whole idea—JSON is the bridge between the server’s data and your Swift model.

Networking in Swift

Why async/await?

Network requests take time—maybe milliseconds, maybe seconds. If we waited synchronously, the entire UI would freeze. Swift’s async/await (introduced in Swift 5.5) lets us “pause” a function in the background until the data arrives, without blocking the main thread.

Analogy

Think of it like ordering food at a restaurant. You place your order (send the request), then keep chatting with friends (the app stays responsive) until the waiter brings the food back (the response arrives).

A GET Request

Here’s the basic pattern for fetching data from a server:

func fetchPosts() async throws -> [Post] {
    let url = URL(string: "https://api.example.com/posts")!
    let (data, _) = try await URLSession.shared.data(from: url)
    let posts = try JSONDecoder().decode([Post].self, from: data)
    return posts
}

Three steps happen here:

1. Build a URL from a string. 2. Use URLSession.shared.data(from:) to fire the request and await the response. 3. Decode the raw Data into your Swift model with JSONDecoder.

Calling it from a View

struct FeedView: View {
    @State private var posts: [Post] = []

    var body: some View {
        List(posts, id: \.username) { post in
            Text("\(post.username) posted a new photo!")
        }
        .task {
            do {
                posts = try await fetchPosts()
            } catch {
                print("Failed to fetch posts: \(error)")
            }
        }
    }
}

The .task modifier kicks off the async work when the view appears. Because it runs asynchronously, the list can render immediately (empty at first) and populate once the data arrives.

Codable

Codable is a protocol (actually a typealias for Decodable & Encodable) that tells Swift how to convert between JSON and your structs. If the JSON keys match your property names, you get automatic decoding for free:

struct Student: Codable {
    let age: Int
    let classes: [String]
    let firstName: String
    let lastName: String
    let major: String
}

Watch Out: snake_case vs. camelCase

APIs often use snake_case keys (e.g. "first_name") while Swift uses camelCase (e.g. firstName). To handle this automatically, set the decoder’s key strategy:

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

Before Lecture

Come to lecture with a rough mental model of these ideas. You don’t need to memorize any syntax—we’ll live-code everything together and do a Postman demo so you can see making server requests in action. Some questions to think about::

Questions to Consider

1. What apps on your phone probably make HTTP requests? What data are they fetching?

2. If URLSession.shared.data(from:) is async, what would happen if it weren’t and you called it on the main thread?

3. Given a JSON object with keys "user_id" and "display_name", what would your Swift struct look like?