Skip to main content
The Networking module is a production-grade HTTP layer with:
  • Type-safe requests/responses
  • Interceptors for auth, headers, retry, and logging
  • Error mapping to app-friendly types
  • Configurable retry (exponential backoff)
  • Optional response caching
Full technical details live in your project at /docs/modules/Networking.md.

Purpose

Handles:
  • HTTP requests via a single HTTPClient API
  • Cross-cutting concerns (auth tokens, headers, retry, logging)
  • Consistent error handling and recoverability

Core Abstractions

HTTPClient Protocol

protocol HTTPClient {
    func execute<R: HTTPRequest>(_ request: R) async throws -> R.Response
}

Request Pattern (example)

struct FetchUserRequest: HTTPRequest {
    typealias Response = User
    
    var path: String { "/users/\(userID)" }
    var method: HTTPMethod { .get }
    
    let userID: String
}

// Usage
let user: User = try await httpClient.execute(FetchUserRequest(userID: "123"))

Interceptors (overview)

Interceptors (overview) Auth: Injects Authorization: Bearer … using a Keychain-backed token provider (no manual header work). Retry: Exponential backoff with jitter; respects Retry-After; retries only idempotent methods. Headers: Adds stable app metadata and any custom headers. Logging: Pluggable diagnostics (disabled in release by default).
Configuration is done in your app’s composition layer. See /docs/modules/Networking.md for wiring examples.

What This Gives You

  • Automatic Authentication
  • Intelligent Retry
  • Consistent Headers
KeychainTokenProvider + AuthInterceptorEvery HTTP request automatically includes auth token:
  • ✅ Reads token from secure iOS Keychain
  • ✅ Adds Authorization: Bearer {token} header
  • ✅ Only if not already present (respects manual overrides)
  • ✅ Returns nil gracefully if no token stored
  • ✅ Thread-safe and tested
No manual token management needed - just make requests!
// Tokens added automatically by interceptor
let request = HTTPRequest(path: "/api/protected", method: .get)
let response = try await httpClient.send(request)
// Authorization header included automatically

Real Production Value

This isn’t a simple example - it’s battle-tested production code:
  • Secure: Tokens in iOS Keychain (not UserDefaults)
  • Tested: Comprehensive unit tests included
  • Resilient: Auto-retry with backoff
  • Maintainable: Clean separation of concerns
  • Extensible: Easy to add custom interceptors
Time saved: 12-20 hours of implementing and testing this yourself.

Customization Examples

Add Custom Request

struct CreateConversationRequest: HTTPRequest {
    typealias Response = ConversationDTO
    
    var path: String { "/conversations" }
    var method: HTTPMethod { .post }
    var body: Data? {
        try? JSONEncoder().encode(CreatePayload(title: title))
    }
    
    let title: String
    
    struct CreatePayload: Encodable {
        let title: String
    }
}

// Use it
let conversation = try await httpClient.execute(
    CreateConversationRequest(title: "New Chat")
)

Add Custom Interceptor

class AnalyticsInterceptor: RequestInterceptor {
    func intercept(_ request: URLRequest) async throws -> URLRequest {
        // Log API call for analytics
        analytics.log(event: "api_call", properties: [
            "path": request.url?.path,
            "method": request.httpMethod
        ])
        return request
    }
}

// Add to client
let interceptors = [
    authInterceptor,
    retryInterceptor,
    analyticsInterceptor  // Your custom interceptor
]

Configuration

Setup in CompositionRoot

// Create HTTP client with interceptors
let httpClient = URLSessionHTTPClient(
    session: URLSession.shared,
    interceptors: [
        AuthInterceptor(tokenProvider: keychainTokenProvider),
        RetryInterceptor(policy: .exponential(maxRetries: 3)),
        HeadersInterceptor(headers: [
            "User-Agent": "SwiftAIBoilerplatePro/1.0",
            "App-Version": appVersion
        ])
    ]
)

Error Handling

All HTTP errors are mapped to AppError:
// Network errors
AppError.network(.noConnection)
AppError.network(.timeout)
AppError.network(.serverError(statusCode: 500))

// Decoding errors
AppError.network(.decodingFailed)

// Unknown errors
AppError.unknown(underlying: error)

Key Files

ComponentLocation
HTTPClientPackages/Networking/Sources/Networking/HTTPClient.swift
InterceptorsPackages/Networking/Sources/Networking/Interceptors/
Retry LogicPackages/Networking/Sources/Networking/Retry/

Dependencies

  • Core - Error handling, logging

Used By

  • Auth - API calls to Supabase
  • AI - Proxy requests
  • Custom services - Your API calls

Best Practices

  • One struct per endpoint
  • Type-safe responses
  • Encodable request bodies
  • Clear naming (verb + noun)
  • Map to AppError
  • Retry idempotent requests
  • Log failures with context
  • Provide user-friendly messages
  • Order matters (auth before retry)
  • Keep interceptors focused
  • Test in isolation
  • Use for cross-cutting concerns

Learn More

Full Documentation

Find complete technical guides inside your project under /docs/modules/Networking.md.

Auth Module

Uses HTTPClient for API calls

AI Module

Uses HTTPClient for proxy

Architecture

See networking layer

Test Coverage

93%+ - Comprehensive test suite Tests include:
  • Request execution
  • Interceptor ordering
  • Retry logic
  • Error scenarios
  • Timeout handling
I