CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-client-websockets-iosx64

Ktor client WebSocket plugin for iOS x64 target providing WebSocket client capabilities for iOS simulator applications using Kotlin Multiplatform.

Pending
Overview
Eval results
Files

message-serialization.mddocs/

Message Serialization

Content conversion support for serializing and deserializing objects to/from WebSocket frames with type safety.

Required Imports

import io.ktor.client.plugins.websocket.*
import io.ktor.util.reflect.*
import io.ktor.serialization.*
import kotlinx.serialization.*

Capabilities

Serialization Functions

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"))
}

Deserialization Functions

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): T

Usage 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>())
}

Content Converter Access

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!")
    }
}

Content Converter Types

WebSocket Content Converter Interface

Base interface for implementing custom content converters.

/**
 * Interface for WebSocket content serialization/deserialization
 */
interface WebsocketContentConverter {
    // Implementation details vary by concrete converter
}

Exception Types

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)

Serialization Examples

JSON Serialization with Kotlinx.serialization

@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
        }
    }
}

Custom Data Types

@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 -> {}
        }
    }
}

Mixed Serialization and Raw Frames

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 -> {}
        }
    }
}

Error Handling

Converter Configuration Errors

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}""")
    }
}

Deserialization Errors

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")
        }
    }
}

Type Safety

// 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

docs

index.md

message-serialization.md

plugin-configuration.md

session-management.md

websocket-connections.md

tile.json