Networking cheat sheet

The 11 Networking interview answers in 30 seconds each, plus quick facts. Printable, free.

One page per topic: print it or save it as PDF from your browser's print menu. Practise these questions

Making a network request

Use URLSession's async API: let (data, response) = try await URLSession.shared.data(from: url). Then check that the response is an HTTPURLResponse with a 2xx status before decoding the body with JSONDecoder. Throw typed errors for bad statuses so callers can show the right message or retry.

enum APIError: Error { case badStatus(Int) }

func loadUser(id: Int) async throws -> User {
    var components = URLComponents(string: "https://api.example.com/users")!
    components.path += "/\(id)"
    let (data, response) = try await URLSession.shared.data(from: components.url!)
    guard let http = response as? HTTPURLResponse else { throw URLError(.badServerResponse) }
    guard (200...299).contains(http.statusCode) else { throw APIError.badStatus(http.statusCode) }
    return try JSONDecoder().decode(User.self, from: data)
}

POST requests with a JSON body

Create a URLRequest, set httpMethod = "POST" and the Content-Type: application/json header, encode your Encodable value with JSONEncoder into httpBody, and send it with URLSession.shared.data(for:). Check the status code, and decode the response if the server returns one.

Codable and mismatched keys

Codable is Encodable plus Decodable; the compiler generates the conversion for types whose properties are all Codable. For different key names, either set keyDecodingStrategy = .convertFromSnakeCase or declare a CodingKeys enum with custom raw values. Don't combine both for the same key: the strategy converts JSON keys first, so a raw value like "user_name" no longer matches.

Retries with exponential backoff

Retry only transient failures: timeouts, lost connections, 5xx responses and 429. Wait longer after each attempt (for example 0.5 s, 1 s, 2 s) plus random jitter, cap the number of attempts, and stop immediately if the task is cancelled. Never retry 4xx client errors, and only retry non-idempotent requests with an idempotency key.

func withRetry<T>(maxAttempts: Int = 3,
                  _ operation: () async throws -> T) async throws -> T {
    var attempt = 0
    while true {
        do {
            return try await operation()
        } catch let error where isTransient(error) && attempt < maxAttempts - 1 {
            attempt += 1
            let base = 0.5 * pow(2, Double(attempt - 1))
            try await Task.sleep(for: .seconds(base + .random(in: 0...0.3)))
        }
    }
}

App Transport Security (ATS)

ATS makes apps use HTTPS with TLS 1.2 or later and strong ciphers for connections made through URLSession. If one legacy host can't comply, add an exception for that domain only under NSExceptionDomains in Info.plist, and plan to remove it. Turning ATS off globally with NSAllowsArbitraryLoads needs a justification in App Review and exposes all traffic.

A testable network layer

Hide URLSession behind a small protocol, for example protocol HTTPClient { func data(for: URLRequest) async throws -> (Data, URLResponse) }. Inject it into services and view models. In production pass URLSession; in tests pass a stub that returns canned responses or errors, so tests are fast, deterministic and offline.

URLSessionConfiguration

.default uses disk caches and the shared cookie and credential stores. .ephemeral keeps everything in memory and discards it when the session ends, which suits private or sign-in flows. .background(withIdentifier:) hands transfers to the system so they continue while the app is suspended. Customize timeouts, headers, cache policy, cellular access and connection limits per session.

Rate limits and temporary outages

Treat 429 and 503 as signals: honour Retry-After, back off with jitter, and share one limiter across the app so every caller slows down together. Add a circuit breaker that stops calls for a while after repeated failures, coalesce duplicate requests, and show cached data with a clear offline or retry state instead of spinning forever.

Resilient large downloads

Use a background URLSession download task so the transfer continues when the app is suspended or terminated. Move the file to permanent storage inside didFinishDownloadingTo before the method returns, because the temporary file is then deleted. Save resume data on failure, check free space first, verify a checksum, and show progress per item.

Reliable photo uploads

Write each photo to a file and queue an upload with a background URLSession uploadTask(with:fromFile:), which keeps going while the app is suspended. Persist the queue with an idempotency key per photo so retries don't duplicate. Compress or resize first, upload in chunks or resumable sessions for big files, and show per-item progress and failures.

A fast image-loading pipeline

Check a memory cache of decoded images first, then a disk cache, and only then download. Coalesce requests for the same URL, downsample to the display size off the main thread, store results in both caches, and deliver on the main actor only if the cell still wants that image. Cancel requests for cells that scroll away and prefetch ones about to appear.

Quick facts

HTTP status classes
1xx informational, 2xx success, 3xx redirect, 4xx client error, 5xx server error. URLSession doesn't throw for any of them.
URLComponents
Builds URLs safely and percent-encodes query items for you.
Low Data Mode
Check allowsConstrainedNetworkAccess; defer prefetches and large media when the user enables it.