
Versatile networking module utilizing Ktor, supporting dynamic HTTP client creation, custom interceptors, SSL pinning, and platform-specific engines for seamless integration in production and testing.
A versatile and robust networking module for Kotlin Multiplatform (KMP) projects, utilizing Ktor with platform-specific engines. This module supports dynamic HTTP client engine creation, including success and failure mock engines, interceptor-based OkHttp for Android, and SSL pinning configurations. It ensures seamless integration and consistent networking functionality across Android and iOS, making it ideal for both production and testing environments.
Current Version:
💡 For compatibility with previous versions, see the CHANGELOG.md for version-specific requirements. We recommend staying within 1-2 minor versions of the latest release for optimal compatibility and security updates.
Add the package to your iOS project using Xcode or Package.swift:
https://github.com/PayDock/android-core-networking
dependencies: [
.package(
url: "https://github.com/PayDock/android-core-networking",
from: "1.0.0"
)
]Then import in your Swift code:
import PaydockNetworking
⚠️ Breaking Change: If migrating from CocoaPods, update your imports fromimport networktoimport PaydockNetworking
mavenCentral() is in your repositories (usually default):repositories {
mavenCentral()
}dependencies {
implementation("com.paydock.core:network:<version>")
}dependencies {
implementation("com.paydock.core:network-android:<version>")
}Latest versions:
Note: No ProGuard/R8 rules are required for this library.
Add the submodule to your repository:
git submodule add https://github.com/PayDock/android-core-networking.gitInclude the submodule in your project's settings.gradle.kts:
include(":network")
project(":network").projectDir = file("path/to/network")Add the network module as a dependency in your module's build.gradle.kts:
dependencies {
implementation(project(":network"))
}Import and use the networking module in your Swift code:
import PaydockNetworking
// Create HTTP client
let builder = NetworkClientBuilder.create()
builder.setBaseUrl("https://api.paydock.com")
builder.setDebug(true) // Enable for development builds
let httpClient = builder.build()
// Make network requests
Task {
do {
let response = try await httpClient.get(url: "/v1/example")
// Handle success
print("Response: \(response)")
} catch let error as ApiException {
// Handle API errors
print("API Error: \(error.message)")
} catch {
// Handle other errors
print("Network Error: \(error)")
}
}Use the NetworkClientBuilder to build an instance of HttpClient:
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("https://example.com")
.setSslPins(listOf("sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
.setDebug(BuildConfig.DEBUG)
.build()For Android-specific customizations, you can cast the builder to AndroidNetworkClientBuilder:
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("example.com")
.setSslPins(listOf("sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")) // optional
.setDebug(BuildConfig.DEBUG)
// Android OKHttp Interceptor
.addInterceptor(CustomInterceptor())
.build()Once you have an instance of HttpClient, you can make network requests as usual with Ktor:
runBlocking {
val response: HttpResponse = httpClient.get("endpoint")
println(response.status)
}import PaydockNetworking
// Create client with authentication
let builder = NetworkClientBuilder.create()
builder.setBaseUrl("https://api.paydock.com")
// Note: iOS authentication is typically handled via request headers
// Add authentication token to individual requests or use interceptors
let httpClient = builder.build()
// Add auth header to requests
Task {
let response = try await httpClient.get(url: "/v1/secure-endpoint") { request in
request.headers.append("Authorization", "Bearer <your_token>")
}
}import com.paydock.core.network.NetworkClientBuilder
import com.paydock.core.network.addInterceptor
import com.paydock.core.network.interceptor.AuthInterceptor
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("api.paydock.com")
.addInterceptor(AuthInterceptor("<public_key>"))
.build()HttpClient is configured with expectSuccess = true and maps API errors to ApiException.
import com.paydock.core.network.exceptions.ApiException
import io.ktor.client.request.get
try {
val response = httpClient.get("/v1/example")
// handle success
} catch (e: ApiException) {
// Displayable message derived from server error
val userMessage = e.message
// Or access full error payload
val error = e.error
}val httpClient = NetworkClientBuilder.create()
.setBaseUrl("api.paydock.com")
.setRequestTimeout(30.0)
.setResponseTimeout(30.0)
.setMaxRetries(2)
.setRetryInterval(2_000)
.build()import com.paydock.core.network.NetworkClientBuilder
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.http.HttpStatusCode
import io.ktor.http.headersOf
val mockEngine = MockEngine { _ ->
respond(
content = """{"type":"ok","data":{}}""",
status = HttpStatusCode.OK,
headers = headersOf("Content-Type", "application/json")
)
}
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("example.com")
.setMockEngine(mockEngine)
.build()The networking module provides full-featured WebSocket support with automatic reconnection, ping/pong handling, and Flow-based message streaming.
import com.paydock.core.network.websocket.WebSocketClientBuilder
import com.paydock.core.network.websocket.WebSocketMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
// Create WebSocket client
val scope = CoroutineScope(Dispatchers.IO)
val wsClient = WebSocketClientBuilder.create()
.setUrl("wss://example.com/ws")
.addHeader("Authorization", "Bearer token123")
.setPingInterval(30_000) // 30 seconds
.setTimeout(60_000) // 60 seconds
.build(scope)
// Connect to WebSocket
scope.launch {
wsClient.connect()
}
// Observe messages
scope.launch {
wsClient.observeMessages().collect { message ->
when (message) {
is WebSocketMessage.Text -> println("Received: ${message.content}")
is WebSocketMessage.Binary -> println("Received binary: ${message.data.size} bytes")
}
}
}
// Send messages
scope.launch {
wsClient.send("Hello, WebSocket!")
}
// Disconnect
scope.launch {
wsClient.disconnect()
}val wsClient = WebSocketClientBuilder.create()
.setUrl("wss://example.com/ws")
.setReconnectOnFailure(true)
.setMaxReconnectAttempts(5)
.setReconnectDelay(5_000) // 5 seconds between attempts
.build(scope)
// Observe connection events
scope.launch {
wsClient.observeEvents().collect { event ->
when (event) {
is WebSocketEvent.Connected -> println("WebSocket connected")
is WebSocketEvent.Disconnected -> println("Disconnected: ${event.reason}")
is WebSocketEvent.Error -> println("Error: ${event.error}")
is WebSocketEvent.Reconnecting -> println("Reconnecting (attempt ${event.attempt})...")
}
}
}import com.paydock.core.network.websocket.WebSocketState
scope.launch {
wsClient.state.collect { state ->
when (state) {
WebSocketState.DISCONNECTED -> println("WebSocket disconnected")
WebSocketState.CONNECTING -> println("WebSocket connecting...")
WebSocketState.CONNECTED -> println("WebSocket connected")
WebSocketState.CLOSING -> println("WebSocket closing...")
WebSocketState.FAILED -> println("WebSocket connection failed")
}
}
}import PaydockNetworking
import Combine
// Create WebSocket client
let builder = WebSocketClientBuilder.create()
builder.setUrl("wss://example.com/ws")
builder.addHeader("Authorization", "Bearer token123")
builder.setPingInterval(30_000)
let wsClient = builder.build(scope: yourCoroutineScope)
// Connect
Task {
try await wsClient.connect()
}
// Observe messages
Task {
for await message in wsClient.observeMessages() {
if let textMessage = message as? WebSocketMessageText {
print("Received: \(textMessage.content)")
}
}
}
// Send message
Task {
try await wsClient.send("Hello, WebSocket!")
}| Option | Description | Default |
|---|---|---|
url |
WebSocket URL (ws:// or wss://) | Required |
headers |
Custom headers for handshake | Empty |
pingIntervalMs |
Interval for ping frames | 30,000ms (30s) |
timeoutMs |
Connection/operation timeout | 30,000ms (30s) |
maxFrameSize |
Maximum WebSocket frame size | 1,048,576 bytes (1MB) |
reconnectOnFailure |
Auto-reconnect on failure | false |
maxReconnectAttempts |
Max reconnection attempts | 3 |
reconnectDelayMs |
Delay between reconnects | 5,000ms (5s) |
import com.paydock.core.network.exceptions.WebSocketException
try {
wsClient.connect()
} catch (e: WebSocketException.ConnectionFailedException) {
println("Failed to connect: ${e.message}")
} catch (e: WebSocketException.NotConnectedException) {
println("WebSocket not connected")
} catch (e: WebSocketException.SendFailedException) {
println("Failed to send message: ${e.message}")
}See CHANGELOG.md for release notes and migration details.
A versatile and robust networking module for Kotlin Multiplatform (KMP) projects, utilizing Ktor with platform-specific engines. This module supports dynamic HTTP client engine creation, including success and failure mock engines, interceptor-based OkHttp for Android, and SSL pinning configurations. It ensures seamless integration and consistent networking functionality across Android and iOS, making it ideal for both production and testing environments.
Current Version:
💡 For compatibility with previous versions, see the CHANGELOG.md for version-specific requirements. We recommend staying within 1-2 minor versions of the latest release for optimal compatibility and security updates.
Add the package to your iOS project using Xcode or Package.swift:
https://github.com/PayDock/android-core-networking
dependencies: [
.package(
url: "https://github.com/PayDock/android-core-networking",
from: "1.0.0"
)
]Then import in your Swift code:
import PaydockNetworking
⚠️ Breaking Change: If migrating from CocoaPods, update your imports fromimport networktoimport PaydockNetworking
mavenCentral() is in your repositories (usually default):repositories {
mavenCentral()
}dependencies {
implementation("com.paydock.core:network:<version>")
}dependencies {
implementation("com.paydock.core:network-android:<version>")
}Latest versions:
Note: No ProGuard/R8 rules are required for this library.
Add the submodule to your repository:
git submodule add https://github.com/PayDock/android-core-networking.gitInclude the submodule in your project's settings.gradle.kts:
include(":network")
project(":network").projectDir = file("path/to/network")Add the network module as a dependency in your module's build.gradle.kts:
dependencies {
implementation(project(":network"))
}Import and use the networking module in your Swift code:
import PaydockNetworking
// Create HTTP client
let builder = NetworkClientBuilder.create()
builder.setBaseUrl("https://api.paydock.com")
builder.setDebug(true) // Enable for development builds
let httpClient = builder.build()
// Make network requests
Task {
do {
let response = try await httpClient.get(url: "/v1/example")
// Handle success
print("Response: \(response)")
} catch let error as ApiException {
// Handle API errors
print("API Error: \(error.message)")
} catch {
// Handle other errors
print("Network Error: \(error)")
}
}Use the NetworkClientBuilder to build an instance of HttpClient:
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("https://example.com")
.setSslPins(listOf("sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="))
.setDebug(BuildConfig.DEBUG)
.build()For Android-specific customizations, you can cast the builder to AndroidNetworkClientBuilder:
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("example.com")
.setSslPins(listOf("sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")) // optional
.setDebug(BuildConfig.DEBUG)
// Android OKHttp Interceptor
.addInterceptor(CustomInterceptor())
.build()Once you have an instance of HttpClient, you can make network requests as usual with Ktor:
runBlocking {
val response: HttpResponse = httpClient.get("endpoint")
println(response.status)
}import PaydockNetworking
// Create client with authentication
let builder = NetworkClientBuilder.create()
builder.setBaseUrl("https://api.paydock.com")
// Note: iOS authentication is typically handled via request headers
// Add authentication token to individual requests or use interceptors
let httpClient = builder.build()
// Add auth header to requests
Task {
let response = try await httpClient.get(url: "/v1/secure-endpoint") { request in
request.headers.append("Authorization", "Bearer <your_token>")
}
}import com.paydock.core.network.NetworkClientBuilder
import com.paydock.core.network.addInterceptor
import com.paydock.core.network.interceptor.AuthInterceptor
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("api.paydock.com")
.addInterceptor(AuthInterceptor("<public_key>"))
.build()HttpClient is configured with expectSuccess = true and maps API errors to ApiException.
import com.paydock.core.network.exceptions.ApiException
import io.ktor.client.request.get
try {
val response = httpClient.get("/v1/example")
// handle success
} catch (e: ApiException) {
// Displayable message derived from server error
val userMessage = e.message
// Or access full error payload
val error = e.error
}val httpClient = NetworkClientBuilder.create()
.setBaseUrl("api.paydock.com")
.setRequestTimeout(30.0)
.setResponseTimeout(30.0)
.setMaxRetries(2)
.setRetryInterval(2_000)
.build()import com.paydock.core.network.NetworkClientBuilder
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.http.HttpStatusCode
import io.ktor.http.headersOf
val mockEngine = MockEngine { _ ->
respond(
content = """{"type":"ok","data":{}}""",
status = HttpStatusCode.OK,
headers = headersOf("Content-Type", "application/json")
)
}
val httpClient = NetworkClientBuilder.create()
.setBaseUrl("example.com")
.setMockEngine(mockEngine)
.build()The networking module provides full-featured WebSocket support with automatic reconnection, ping/pong handling, and Flow-based message streaming.
import com.paydock.core.network.websocket.WebSocketClientBuilder
import com.paydock.core.network.websocket.WebSocketMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
// Create WebSocket client
val scope = CoroutineScope(Dispatchers.IO)
val wsClient = WebSocketClientBuilder.create()
.setUrl("wss://example.com/ws")
.addHeader("Authorization", "Bearer token123")
.setPingInterval(30_000) // 30 seconds
.setTimeout(60_000) // 60 seconds
.build(scope)
// Connect to WebSocket
scope.launch {
wsClient.connect()
}
// Observe messages
scope.launch {
wsClient.observeMessages().collect { message ->
when (message) {
is WebSocketMessage.Text -> println("Received: ${message.content}")
is WebSocketMessage.Binary -> println("Received binary: ${message.data.size} bytes")
}
}
}
// Send messages
scope.launch {
wsClient.send("Hello, WebSocket!")
}
// Disconnect
scope.launch {
wsClient.disconnect()
}val wsClient = WebSocketClientBuilder.create()
.setUrl("wss://example.com/ws")
.setReconnectOnFailure(true)
.setMaxReconnectAttempts(5)
.setReconnectDelay(5_000) // 5 seconds between attempts
.build(scope)
// Observe connection events
scope.launch {
wsClient.observeEvents().collect { event ->
when (event) {
is WebSocketEvent.Connected -> println("WebSocket connected")
is WebSocketEvent.Disconnected -> println("Disconnected: ${event.reason}")
is WebSocketEvent.Error -> println("Error: ${event.error}")
is WebSocketEvent.Reconnecting -> println("Reconnecting (attempt ${event.attempt})...")
}
}
}import com.paydock.core.network.websocket.WebSocketState
scope.launch {
wsClient.state.collect { state ->
when (state) {
WebSocketState.DISCONNECTED -> println("WebSocket disconnected")
WebSocketState.CONNECTING -> println("WebSocket connecting...")
WebSocketState.CONNECTED -> println("WebSocket connected")
WebSocketState.CLOSING -> println("WebSocket closing...")
WebSocketState.FAILED -> println("WebSocket connection failed")
}
}
}import PaydockNetworking
import Combine
// Create WebSocket client
let builder = WebSocketClientBuilder.create()
builder.setUrl("wss://example.com/ws")
builder.addHeader("Authorization", "Bearer token123")
builder.setPingInterval(30_000)
let wsClient = builder.build(scope: yourCoroutineScope)
// Connect
Task {
try await wsClient.connect()
}
// Observe messages
Task {
for await message in wsClient.observeMessages() {
if let textMessage = message as? WebSocketMessageText {
print("Received: \(textMessage.content)")
}
}
}
// Send message
Task {
try await wsClient.send("Hello, WebSocket!")
}| Option | Description | Default |
|---|---|---|
url |
WebSocket URL (ws:// or wss://) | Required |
headers |
Custom headers for handshake | Empty |
pingIntervalMs |
Interval for ping frames | 30,000ms (30s) |
timeoutMs |
Connection/operation timeout | 30,000ms (30s) |
maxFrameSize |
Maximum WebSocket frame size | 1,048,576 bytes (1MB) |
reconnectOnFailure |
Auto-reconnect on failure | false |
maxReconnectAttempts |
Max reconnection attempts | 3 |
reconnectDelayMs |
Delay between reconnects | 5,000ms (5s) |
import com.paydock.core.network.exceptions.WebSocketException
try {
wsClient.connect()
} catch (e: WebSocketException.ConnectionFailedException) {
println("Failed to connect: ${e.message}")
} catch (e: WebSocketException.NotConnectedException) {
println("WebSocket not connected")
} catch (e: WebSocketException.SendFailedException) {
println("Failed to send message: ${e.message}")
}See CHANGELOG.md for release notes and migration details.