Ktor client WebSocket plugin for iOS x64 target providing WebSocket client capabilities for iOS simulator applications using Kotlin Multiplatform.
—
Content conversion support for serializing and deserializing objects to/from WebSocket frames with type safety.
import io.ktor.client.plugins.websocket.*
import io.ktor.util.reflect.*
import io.ktor.serialization.*
import kotlinx.serialization.*Type-safe functions for sending serialized data through WebSocket connections.
/**
* Serializes data to a frame and enqueues it using reified type information
* @param data The data object to serialize and send
* @throws WebsocketConverterNotFoundException if no contentConverter is configured
*/
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(data: T)
/**
* Serializes data to a frame and enqueues it using explicit type information
* @param data The data object to serialize and send
* @param typeInfo Type information for the data object
* @throws WebsocketConverterNotFoundException if no contentConverter is configured
*/
suspend fun DefaultClientWebSocketSession.sendSerialized(data: Any?, typeInfo: TypeInfo)Usage Examples:
// Configure client with content converter
val client = HttpClient {
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
@Serializable
data class Message(val text: String, val timestamp: Long)
client.webSocket("ws://example.com") {
// Send serialized object using reified type
val message = Message("Hello!", System.currentTimeMillis())
sendSerialized(message)
// Send serialized object using explicit type info
sendSerialized(message, typeInfo<Message>())
// Send different types
sendSerialized(42)
sendSerialized(listOf("a", "b", "c"))
sendSerialized(mapOf("key" to "value"))
}Type-safe functions for receiving and deserializing data from WebSocket frames.
/**
* Dequeues a frame and deserializes it using reified type information
* @return Deserialized object of type T
* @throws WebsocketConverterNotFoundException if no contentConverter is configured
* @throws WebsocketDeserializeException if deserialization fails
*/
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): T
/**
* Dequeues a frame and deserializes it using explicit type information
* @param typeInfo Type information for deserialization target
* @return Deserialized object of type T
* @throws WebsocketConverterNotFoundException if no contentConverter is configured
* @throws WebsocketDeserializeException if deserialization fails
*/
suspend fun <T> DefaultClientWebSocketSession.receiveDeserialized(typeInfo: TypeInfo): TUsage Examples:
@Serializable
data class Response(val status: String, val data: String)
client.webSocket("ws://example.com") {
// Send a request
sendSerialized(Message("ping", System.currentTimeMillis()))
// Receive and deserialize response
val response = receiveDeserialized<Response>()
println("Status: ${response.status}, Data: ${response.data}")
// Receive different types
val number = receiveDeserialized<Int>()
val list = receiveDeserialized<List<String>>()
val map = receiveDeserialized<Map<String, Any>>()
// Using explicit type info
val explicitResponse = receiveDeserialized<Response>(typeInfo<Response>())
}Access to the configured content converter for advanced usage.
/**
* Gets the WebSocket content converter from the client plugin configuration
* @return The configured WebsocketContentConverter or null if none configured
*/
val DefaultClientWebSocketSession.converter: WebsocketContentConverter?Usage Examples:
client.webSocket("ws://example.com") {
val converter = converter
if (converter != null) {
println("Content converter available: ${converter::class.simpleName}")
// Can perform serialization/deserialization operations
sendSerialized("Hello World!")
} else {
println("No content converter configured")
// Must use raw frame sending
send("Hello World!")
}
}Base interface for implementing custom content converters.
/**
* Interface for WebSocket content serialization/deserialization
*/
interface WebsocketContentConverter {
// Implementation details vary by concrete converter
}Exceptions thrown during serialization/deserialization operations.
/**
* Exception thrown when no suitable content converter is found
* @param message Error description
*/
class WebsocketConverterNotFoundException(message: String) : Exception(message)
/**
* Exception thrown when frame deserialization fails
* @param message Error description
* @param frame The WebSocket frame that failed to deserialize
*/
class WebsocketDeserializeException(message: String, val frame: Frame) : Exception(message)@Serializable
data class ChatMessage(
val user: String,
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
@Serializable
data class ChatResponse(
val type: String,
val payload: ChatMessage
)
val client = HttpClient {
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json {
prettyPrint = true
ignoreUnknownKeys = true
})
}
}
client.webSocket("ws://chat.example.com") {
// Send chat message
sendSerialized(ChatMessage("user123", "Hello everyone!"))
// Receive responses
while (true) {
try {
val response = receiveDeserialized<ChatResponse>()
when (response.type) {
"message" -> println("${response.payload.user}: ${response.payload.message}")
"notification" -> println("System: ${response.payload.message}")
}
} catch (e: ClosedReceiveChannelException) {
break
}
}
}@Serializable
data class WebSocketCommand(
val action: String,
val parameters: Map<String, String> = emptyMap()
)
@Serializable
data class WebSocketEvent(
val event: String,
val data: JsonElement
)
client.webSocket("ws://api.example.com") {
// Send command
sendSerialized(WebSocketCommand(
action = "subscribe",
parameters = mapOf("channel" to "updates")
))
// Process events
for (frame in incoming) {
when (frame) {
is Frame.Text, is Frame.Binary -> {
try {
val event = receiveDeserialized<WebSocketEvent>()
handleEvent(event.event, event.data)
} catch (e: WebsocketDeserializeException) {
println("Failed to deserialize frame: ${e.message}")
// Handle raw frame if needed
if (frame is Frame.Text) {
println("Raw text: ${frame.readText()}")
}
}
}
is Frame.Close -> break
else -> {}
}
}
}client.webSocket("ws://example.com") {
// Send serialized object
sendSerialized(Message("Hello", 123))
// Send raw text frame
send("Raw text message")
// Send raw binary frame
send(byteArrayOf(1, 2, 3, 4))
// Receive and handle mixed content
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
val text = frame.readText()
if (text.startsWith("{")) {
// Assume JSON, try to deserialize
try {
val message = receiveDeserialized<Message>()
println("Deserialized: $message")
} catch (e: WebsocketDeserializeException) {
println("Raw text: $text")
}
} else {
println("Raw text: $text")
}
}
is Frame.Binary -> {
try {
val data = receiveDeserialized<ByteArray>()
println("Deserialized binary: ${data.size} bytes")
} catch (e: WebsocketDeserializeException) {
println("Raw binary: ${frame.data.size} bytes")
}
}
is Frame.Close -> break
else -> {}
}
}
}client.webSocket("ws://example.com") {
try {
sendSerialized(Message("test", 123))
} catch (e: WebsocketConverterNotFoundException) {
println("No content converter configured: ${e.message}")
// Fall back to raw frame sending
send("""{"text":"test","id":123}""")
}
}client.webSocket("ws://example.com") {
try {
val message = receiveDeserialized<Message>()
println("Received: $message")
} catch (e: WebsocketDeserializeException) {
println("Failed to deserialize frame: ${e.message}")
println("Frame type: ${e.frame::class.simpleName}")
// Handle raw frame content
when (val frame = e.frame) {
is Frame.Text -> println("Raw text: ${frame.readText()}")
is Frame.Binary -> println("Raw binary: ${frame.data.size} bytes")
}
}
}// Compile-time type safety
val message: Message = receiveDeserialized<Message>()
// Runtime type checking
inline fun <reified T> safeReceiveDeserialized(): T? {
return try {
receiveDeserialized<T>()
} catch (e: WebsocketDeserializeException) {
null
}
}
val maybeMessage = safeReceiveDeserialized<Message>()
if (maybeMessage != null) {
println("Successfully received: $maybeMessage")
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-websockets-iosx64