A lightweight Kotlin Multiplatform HTTP client built on Ktor.
KMP HTTP Client provides a minimal, builder-style API for HTTP on Android and iOS. It offers typed configuration, pluggable interceptors (logging, auth, error handling), and a unified response model—reducing boilerplate and keeping a single, consistent API across platforms.
- Overview
- Features
- Requirements
- Installation
- Quick Start
- Usage
- Project Structure
- Best Practices
- Tech Support
- References
| Item | Description |
|---|---|
| Purpose | Simplify HTTP in Kotlin Multiplatform (Android & iOS) with one API surface. |
| Problem | Avoid duplicated Ktor setup per platform, standardize request/response handling, and centralize cross-cutting concerns (auth, logging, errors). |
| Solution | A thin wrapper over Ktor with a builder API, shared config, and interceptors. |
Core API (Kotlin):
- Entry point:
HttpClient(default singleton + custom instances) - Request:
HttpRequest(builder) - Response:
HttpResponse - Configuration:
HttpClientConfig - Interceptors:
com.santimattius.http.interceptor.*
- Builder-style
HttpRequestwith path, query, headers, and body - Typed
HttpClientConfig(timeouts, logging, cache) - Built-in interceptors: logging, auth, token refresh, error handling
- Custom interceptors via
Interceptor(Kotlin) / Swift protocol - Unified
HttpResponsewith success/error and optional typed body - Android: Ktor OkHttp engine; iOS: Ktor Darwin
- Swift interoperability via Skie for cleaner iOS usage
| Platform | Requirement |
|---|---|
| Kotlin | Kotlin Multiplatform project (Kotlin 2.x) |
| Android | minSdk / compileSdk as in shared/build.gradle.kts; INTERNET permission |
| iOS | Xcode, iOS 14+; integration via framework or Swift Package Manager |
| Dependencies | Ktor and Kotlinx Serialization (included in shared module) |
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.santimattius.kmp:http-client:<version>")
}- Engine: Ktor OkHttp (
ktor-client-okhttp). - Initialize once (e.g.
Application#onCreateor AndroidX Startup).
The shared module produces the KMPHttpClient framework (baseName in shared/build.gradle.kts). You can:
- Xcode + KMP: Include the
sharedmodule and use the Gradle-generated framework. - Prebuilt XCFramework: Build
KMPHttpClient.xcframeworkand add it to the iOS app. - Swift Package Manager (binary): Add a binary target pointing to the published XCFramework.
Example — SwiftPM binary target:
// Package.swift
import PackageDescription
let package = Package(
name: "KMPHttpClient",
platforms: [.iOS(.v14)],
products: [.library(name: "KMPHttpClient", targets: ["KMPHttpClient"])],
targets: [
.binaryTarget(
name: "KMPHttpClient",
url: "https://github.com/your-org/kmp-http-client/releases/download/1.0.0/KMPHttpClient.xcframework.zip",
checksum: "<swiftpm-checksum>"
)
]
)In Swift:
import KMPHttpClientSkie is enabled in the shared module for better Swift interop:
skie {
swiftBundling { enabled = true }
}- Initialize the client once at app startup.
- Reuse the default client or create custom instances with
HttpClient.create(...).
Kotlin (Android):
import com.santimattius.http.HttpClient
import com.santimattius.http.HttpRequest
import com.santimattius.http.config.HttpClientConfig
// e.g. in Application.onCreate
HttpClient.initialize(
HttpClientConfig(baseUrl = "https://api.example.com")
.connectTimeout(30_000)
.socketTimeout(30_000)
.enableLogging(true)
)
val client = HttpClient.defaultClient()
suspend fun fetchUsers(): Result<String> = runCatching {
val request = HttpRequest
.get("/users")
.queryParam("page", "1")
.header("Accept", "application/json")
.build()
val response = client.execute(request)
if (!response.isSuccessful) error("HTTP ${response.status}")
response.body ?: ""
}Main HttpClientConfig options:
| Parameter | Description |
|---|---|
baseUrl |
Base URL for requests |
connectTimeout |
Connection timeout (ms) |
socketTimeout |
Read/write timeout (ms) |
enableLogging |
Enable/disable HTTP logging |
logLevel |
NONE, BASIC, HEADERS, BODY |
cache |
Optional cache config (CacheConfig) |
import kotlinx.serialization.Serializable
@Serializable
data class LoginRequest(val email: String, val password: String)
suspend fun login(email: String, password: String): Boolean {
val client = HttpClient.defaultClient()
val request = HttpRequest
.post("https://api.example.com")
.path("/auth/login")
.header("Content-Type", "application/json")
.body(LoginRequest(email, password))
.build()
val response = client.execute(request)
return response.isSuccessful
}Built-in interceptors:
- AuthInterceptor — Authorization headers
- TokenRefreshInterceptor — Token refresh flow
- LoggingInterceptor — Configurable logging
- ErrorHandlingInterceptor — Map HTTP errors to exceptions
import com.santimattius.http.config.HttpClientConfig
import com.santimattius.http.config.LogLevel
import com.santimattius.http.interceptor.LoggingInterceptor
val customClient = HttpClient.create(
HttpClientConfig(baseUrl = "https://api.example.com")
.enableLogging(true)
.logLevel(LogLevel.BODY)
).addInterceptors(LoggingInterceptor())Custom interceptor (Swift):
import KMPHttpClient
class OkHttpInterceptor: Interceptor {
func __intercept(chain: any InterceptorChain) async throws -> HttpResponse {
print("Hello from OkHttpInterceptor")
return try await chain.proceed(request: chain.request)
}
}import Foundation
import KMPHttpClient
struct User: Decodable { let id: Int; let name: String }
@MainActor
func loadUsers() async throws -> [User] {
let request = HttpRequest.companion.get("https://api.example.com")
.path("/users")
.header(name: "Accept", value: "application/json")
.build()
let client = HttpClient.shared.defaultClient()
let response = try await client.execute(request: request)
return try response.getBodyAs([User].self)
}| Path | Description |
|---|---|
shared/src/commonMain/kotlin/com/santimattius/http/ |
Core HTTP API, config, interceptors |
shared/src/commonMain/swift/ |
Swift extensions and helpers |
androidApp/ |
Android sample app |
iosApp/ |
iOS sample app |
- Initialization: Call
HttpClient.initialize(...)once at startup; reuseHttpClient.defaultClient(). - URLs: Prefer a single
baseUrland build paths withget("/segment")andqueryParam(). - Timeouts: Set
connectTimeoutandsocketTimeoutto match your backend and network. - Logging: Use
enableLogging(true)andLogLevel.BODYonly in development to avoid leaking sensitive data. - Cross-cutting logic: Use interceptors for auth, retries, and error handling.
- Responses: Always check
HttpResponse.isSuccessfuland handle errors explicitly. - Android: Keep I/O off the main thread (coroutines + appropriate dispatchers).
- iOS: Use
async/await; consider Kotlin facades for suspend functions called from Swift.
Common pitfalls:
| Pitfall | Mitigation |
|---|---|
Using defaultClient() before initialize(...) |
Initialize in app startup (e.g. Application#onCreate). |
| Malformed URLs | Use path("/segment") and validate base URL. |
Missing JSON Content-Type |
Set header("Content-Type", "application/json") for JSON bodies. |
| Verbose logging in production | Use LogLevel.BASIC or NONE. |
| Swift/suspend bridging issues | Verify Skie/interop setup or add Kotlin facades. |
This project is maintained on a best-effort basis by the core team and community. The following guidelines set expectations for support and maintenance.
| Type | In scope | Out of scope |
|---|---|---|
| Library usage | Setup, API usage, migration from Ktor | App-level architecture, non-HTTP bugs |
| Bug reports | Reproducible bugs in this repo | Third-party libs, OS/IDE issues |
| Documentation | README, code samples, migration notes | Custom tutorials, external blogs |
- GitHub Issues: Bug reports and feature requests. Use the issue templates when available.
- GitHub Discussions: Questions, ideas, and community help (no guaranteed response time).
- Search existing Issues and Discussions.
- Read this README and the referenced docs.
- Open an Issue for bugs (with minimal repro) or start a Discussion for questions.
- Be specific: environment, versions, code snippet, and what you already tried.
- Repository: github.com/santimattius/kmp-http-client
- Kotlin Multiplatform: Get started with Kotlin Multiplatform
- Ktor Client: Ktor client documentation
- Environment: Run kdoctor to verify your KMP development setup.