Networking with URLSession & async/await
Fetch real data from the web. You'll call URLSession.shared.data(from:) with async/await , decode JSON into Swift types with Codable , check HTTP status codes, and handle errors cleanly.
Learn Networking with URLSession & async/await in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise…
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Fetch data with URLSession.shared.data(from:)
- • Decode JSON into models using Codable and JSONDecoder
- • Read HTTP status codes via HTTPURLResponse
- • Treat 2xx as success and reject other ranges
- • Handle URLError and DecodingError separately
- • Write clean async throws fetching functions
📊 The Networking Pieces
Piece
Role
URL
Where to fetch from
data(from:)
Async fetch, returns (Data, URLResponse)
HTTPURLResponse
Exposes the statusCode
Codable
Model that maps to JSON
JSONDecoder
Turns Data into your model
1️⃣ Fetch and Decode
The core flow is three lines: build a URL , await the data, and decode it into a Codable model whose properties match the JSON keys.
Your turn. Fill in the protocol, the await keyword, and the decoder type.
2️⃣ Check the Status Code
A 404 or 500 still returns Data rather than throwing, so always cast to HTTPURLResponse and confirm the code is in the 200..<300 range before trusting the body.
3️⃣ Handle Errors
Networking can fail in two ways: the request itself ( URLError ) or the parsing ( DecodingError ). Catch them separately so you can show the right message.
Now you try. Fill in the error-handling keywords.
Pro Tips
- 💡 Always check the status code. A non-2xx response still returns bytes — don't assume success.
- 💡 Match JSON keys exactly, or use CodingKeys / keyDecodingStrategy to bridge naming.
- 💡 Keep fetching off the main thread. await frees the thread automatically.
- 💡 Surface errors to the user, distinguishing "no connection" from "bad data".
Common Errors (and the fix)
- "keyNotFound" DecodingError — a JSON key doesn't match your property; add CodingKeys or fix the name.
- "typeMismatch" DecodingError — e.g. a number arrived as a String; align the property type.
- Treating a 404 as success — you skipped the status check; cast to HTTPURLResponse .
- "Call can throw but is not marked with 'try'" — add try before await on the request and the decode.
📋 Quick Reference
Step
Code
Result
Build URL
URL(string: "…")!
an endpoint
Fetch
try await …data(from:url)
(Data,Response)
Status
resp as? HTTPURLResponse
statusCode
Decode
JSONDecoder().decode(T.self,…)
a model
Errors
do { try … } catch { … }
handled
Frequently Asked Questions
Mini-Challenge: Fetch and Validate
Write a full fetch that checks the status code and decodes a Todo, then report whether it's completed.
🎉 Lesson Complete!
- ✅ URLSession.shared.data(from:) fetches with async/await
- ✅ It returns (Data, URLResponse) and can throw
- ✅ Decode with JSONDecoder into a Codable model
- ✅ Cast to HTTPURLResponse and check the 2xx range
- ✅ Handle URLError and DecodingError separately
- ✅ Next lesson: Persistence with Core Data
Practice quiz
Which URLSession method fetches data using async/await?
- URLSession.shared.data(from:)
- URLSession.fetch()
- URLSession.get()
- URLSession.load()
Answer: URLSession.shared.data(from:). data(from:) is the async method that returns (Data, URLResponse).
What does URLSession.shared.data(from:) return?
- Just Data
- A String
- A tuple of (Data, URLResponse)
- An Int status code
Answer: A tuple of (Data, URLResponse). It returns a tuple containing the response Data and the URLResponse.
Which protocol must a type adopt to be decoded from JSON?
- Hashable
- Decodable (or Codable)
- Equatable
- Identifiable
Answer: Decodable (or Codable). Decodable enables decoding; Codable is Decodable plus Encodable.
Which object turns JSON Data into a Swift value?
- JSONSerialization only
- JSONEncoder
- PropertyListDecoder
- JSONDecoder
Answer: JSONDecoder. JSONDecoder().decode(_:from:) decodes JSON Data into a Decodable type.
To read the HTTP status code, you cast the response to...
- URLRequest
- HTTPURLResponse
- URLComponents
- URLCache
Answer: HTTPURLResponse. Casting URLResponse to HTTPURLResponse exposes the statusCode.
Which status code range generally indicates success?
- 100–199
- 400–499
- 200–299
- 500–599
Answer: 200–299. 2xx codes indicate a successful HTTP response.
How do you handle errors from data(from:)?
- Use try/catch because it can throw
- Ignore them
- Use a guard with no try
- They never fail
Answer: Use try/catch because it can throw. data(from:) is async throws, so you call it with try inside a do/catch.
What does a URLError typically represent?
- A decoding mistake
- A nil optional
- A type cast error
- A networking failure such as no connection
Answer: A networking failure such as no connection. URLError describes networking-level failures like timeouts or no internet.
What does JSONDecoder throw on a shape mismatch?
- Nothing
- A DecodingError
- A URLError
- A fatalError
Answer: A DecodingError. Mismatched JSON throws a DecodingError such as keyNotFound or typeMismatch.
Why mark a fetching function async throws?
- For styling
- To run on the main thread
- So it can await the request and propagate failures
- To make it Sendable
Answer: So it can await the request and propagate failures. async lets it await the network call; throws lets it forward errors to the caller.
Continue this course
- Previous: Advanced Concurrency
- Next: Persistence with Core Data