Browse Questions

Answer: Use a protocol for URLSession or inject a custom URLProtocol subclass that intercepts requests and returns fake responses without hitting the network.

Code Example:

// Protocol approach — most common with async/await
protocol HTTPClient {
    func data(from url: URL) async throws -> (Data, URLResponse)
}

extension URLSession: HTTPClient {}

// Mock for tests
class MockHTTPClient: HTTPClient {
    var mockData: Data = Data()
    var mockResponse: URLResponse = HTTPURLResponse(
        url: URL(string: "https://example.com")!,
        statusCode: 200, httpVersion: nil, headerFields: nil
    )!

    func data(from url: URL) async throws -> (Data, URLResponse) {
        return (mockData, mockResponse)
    }
}

// Inject into your service
class UserService {
    private let client: HTTPClient
    init(client: HTTPClient = URLSession.shared) { self.client = client }
}

// In tests
let mock = MockHTTPClient()
mock.mockData = try! JSONEncoder().encode(User.mock())
let service = UserService(client: mock)