Making HTTP Requests
What is URLSession?
URLSession is Apple's framework for making HTTP requests. It handles downloading data from URLs, uploading data to servers, and downloading files in the background.
With Swift's async/await, URLSession becomes significantly easier to use than the older delegate-based approach.
Basic GET Request
The simplest way to fetch data:
import Foundation
func fetchData() async throws -> Data {
let url = URL(string: "https://api.example.com/users")!
let (data, response) = try await URLSession.shared.data(from: url)
return data
}
The data(from:) method returns a tuple of (Data, URLResponse) and throws on network errors.
Creating URLRequests
For more control, create a URLRequest:
var request = URLRequest(url: URL(string: "https://api.example.com/users")!)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.timeoutInterval = 30
let (data, response) = try await URLSession.shared.data(for: request)
POST Request with Body
Send data to a server:
struct CreateUser: Codable {
let name: String
let email: String
}
func createUser(_ user: CreateUser) async throws -> Data {
let url = URL(string: "https://api.example.com/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(user)
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
PUT, PATCH, DELETE
The same pattern applies for other HTTP methods:
var putRequest = URLRequest(url: url)
putRequest.httpMethod = "PUT"
putRequest.httpBody = try JSONEncoder().encode(updatedUser)
let (putData, _) = try await URLSession.shared.data(for: putRequest)
var deleteRequest = URLRequest(url: url)
deleteRequest.httpMethod = "DELETE"
let (deleteData, _) = try await URLSession.shared.data(for: deleteRequest)
Downloading Files
For large files, use the download method:
let (fileURL, response) = try await URLSession.shared.download(from: url)
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destinationURL = documentsPath.appendingPathComponent("file.zip")
try FileManager.default.moveItem(at: fileURL, to: destinationURL)
Handling Responses
HTTPURLResponse
After making a request, you receive an HTTPURLResponse containing metadata:
let (data, response) = try await URLSession.shared.data(from: url)
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
print("Headers: \(httpResponse.allHeaderFields)")
}
Status Code Handling
Check the status code to determine success or failure:
func fetchData() async throws -> Data {
let url = URL(string: "https://api.example.com/data")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
return data
case 401:
throw NetworkError.unauthorized
case 404:
throw NetworkError.notFound
case 500...599:
throw NetworkError.serverError(statusCode: httpResponse.statusCode)
default:
throw NetworkError.unexpectedStatusCode(httpResponse.statusCode)
}
}
Response Validation
Create a reusable response validator:
extension URLSession {
func validatedData(from request: URLRequest) async throws -> Data {
let (data, response) = try await data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
guard (200...299).contains(httpResponse.statusCode) else {
throw NetworkError.httpError(statusCode: httpResponse.statusCode, data: data)
}
return data
}
}
Error Types
Define custom error types for your networking layer:
enum NetworkError: Error, LocalizedError {
case invalidURL
case invalidResponse
case unauthorized
case notFound
case httpError(statusCode: Int, data: Data)
case serverError(statusCode: Int)
case decodingError(Error)
case unexpectedStatusCode(Int)
var errorDescription: String? {
switch self {
case .invalidURL: return "Invalid URL"
case .invalidResponse: return "Invalid response"
case .unauthorized: return "Unauthorized access"
case .notFound: return "Resource not found"
case .httpError(let code, _): return "HTTP error \(code)"
case .serverError(let code): return "Server error \(code)"
case .decodingError(let error): return "Decoding error: \(error.localizedDescription)"
case .unexpectedStatusCode(let code): return "Unexpected status code \(code)"
}
}
}
Session Configuration
URLSession Configuration
URLSession can be configured with different settings for caching, timeouts, cookies, and more.
Default Configuration
The shared session uses a default configuration:
let session = URLSession.shared
This uses a system-managed cache and default timeout of 60 seconds.
Ephemeral Configuration
No caching, no cookies - ideal for private requests:
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForRequest = 30
config.timeoutIntervalForResource = 300
let session = URLSession(configuration: config)
Background Configuration
For large downloads that should continue in the background:
let config = URLSessionConfiguration.background(withIdentifier: "com.app.download")
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false
let session = URLSession(configuration: config, delegate: downloadDelegate, delegateQueue: nil)
Custom Configuration
Fine-tune session behavior:
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 15
config.timeoutIntervalForResource = 600
config.waitsForConnectivity = true
config.allowsCellularAccess = false
config.httpAdditionalHeaders = [
"X-App-Version": "1.0",
"X-Platform": "iOS"
]
let session = URLSession(configuration: config)
URLCache Configuration
Control caching behavior:
let config = URLSessionConfiguration.default
let cache = URLCache(
memoryCapacity: 50 * 1024 * 1024,
diskCapacity: 200 * 1024 * 1024,
diskPath: "network-cache"
)
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
let session = URLSession(configuration: config)
Session Lifecycle
Sessions are long-lived. Create them once and reuse:
final class NetworkService {
static let shared = NetworkService()
private let session: URLSession
private init() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
session = URLSession(configuration: config)
}
}
Quiz
1. What does URLSession.shared.data(from:) return?
2. Which URLSession configuration has no caching or cookies?
3. What HTTP status code range indicates success?
4. How do you set the HTTP method on a URLRequest?
Flashcards
Question
What is the async/await method for fetching data?
Click to reveal answer
Answer
URLSession.shared.data(from: url) returns (Data, URLResponse) and throws errors.
Question
What is URLSessionConfiguration.ephemeral?
Click to reveal answer
Answer
Sessions with no caching, no cookies, and no persistent storage. Ideal for private requests.
Question
How do you set a timeout on a URLRequest?
Click to reveal answer
Answer
Set request.timeoutInterval to a TimeInterval in seconds.
Question
What does URLSession.shared represent?
Click to reveal answer
Answer
A system-managed singleton session with default configuration that cannot be invalidated.
Revision Notes
Key Takeaways
- 1. URLSession.shared.data(from:) is the primary async method for HTTP requests
- 2. URLResponse must be cast to HTTPURLResponse for status codes and headers
- 3. URLSessionConfiguration.ephemeral provides no persistent storage
- 4. Create URLSession instances once and reuse them as singletons
- 5. Define custom NetworkError enums for structured error handling
Interview Tips
- • Know the difference between URLSession.shared and custom sessions
- • Explain when to use ephemeral vs background configurations
- • Describe how to validate HTTP responses and handle status codes
- • Discuss URLCache configuration for offline support
Cheat Sheet
URLSession.shared.data(from:) - async GET request. URLSession.shared.data(for:) - async request with URLRequest. HTTPURLResponse - status code, headers, mime type. URLSessionConfiguration: .default, .ephemeral, .background.