Skip to content
beginner Phase 5 · Networking & Data

URLSession

Make HTTP requests, handle responses, and manage sessions with URLSession.

50m
3 problems
Topic Progress 0%

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?

Question 1 options

2. Which URLSession configuration has no caching or cookies?

Question 2 options

3. What HTTP status code range indicates success?

Question 3 options

4. How do you set the HTTP method on a URLRequest?

Question 4 options

Flashcards

Question

What is the async/await method for fetching data?

Answer

URLSession.shared.data(from: url) returns (Data, URLResponse) and throws errors.

Question

What is URLSessionConfiguration.ephemeral?

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?

Answer

Set request.timeoutInterval to a TimeInterval in seconds.

Question

What does URLSession.shared represent?

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.