> ## Documentation Index
> Fetch the complete documentation index at: https://docs.insforge.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin 实时 SDK 参考

> 通过 Socket.IO 连接，订阅频道，发布事件，跟踪在线状态，并使用 InsForge Kotlin 实时客户端处理重新连接。

## 安装

1. Add InsForge dependencies to your project

<Tabs>
  <Tab title="Maven Central">
    build.gradle.kts:

    ```kotlin theme={null}
    repositories {
        mavenLocal() // For local development
        mavenCentral()
    }

    dependencies {
        implementation("dev.insforge:insforge-kotlin:0.1.6")
    }
    ```
  </Tab>

  <Tab title="GitHub Packages">
    First, create a GitHub Personal Access Token:

    * Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
    * Select permission: `read:packages`

    Then configure your project using one of the following methods:

    <AccordionGroup>
      <Accordion title="Option A: Environment Variables">
        settings.gradle.kts (or build.gradle.kts):

        ```kotlin theme={null}
        repositories {
            mavenCentral()
            maven {
                url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
                credentials {
                    username = System.getenv("GITHUB_USER") ?: ""
                    password = System.getenv("GITHUB_TOKEN") ?: ""
                }
            }
        }
        ```

        build.gradle.kts:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        Set environment variables before building:

        ```bash theme={null}
        export GITHUB_USER="your-github-username"
        export GITHUB_TOKEN="your-personal-access-token"
        ```
      </Accordion>

      <Accordion title="Option B: gradle.properties (Local Development)">
        Add credentials to your global Gradle properties file:

        \~/.gradle/gradle.properties:

        ```properties theme={null}
        gpr.user=your-github-username
        gpr.token=ghp_xxxxxxxxxxxx
        ```

        settings.gradle.kts:

        ```kotlin theme={null}
        repositories {
            mavenCentral()
            maven {
                url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
                credentials {
                    username = providers.gradleProperty("gpr.user").orNull ?: ""
                    password = providers.gradleProperty("gpr.token").orNull ?: ""
                }
            }
        }
        ```

        build.gradle.kts:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        <Note>
          The `~/.gradle/gradle.properties` file is stored outside your project, so credentials won't be accidentally committed to version control.
        </Note>
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

2. Initialize InsForge SDK

```kotlin theme={null}
import dev.insforge.createInsforgeClient
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.storage.Storage
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.ai.AI

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    install(Auth)
    install(Database)
    install(Storage)
    install(Functions)
    install(Realtime) {
        autoReconnect = true
        reconnectDelay = 5000
    }
    install(AI)
}
```

3. Enable Logging (Optional)

For debugging, you can configure the SDK log level:

```kotlin theme={null}
import dev.insforge.InsforgeLogLevel

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    // DEBUG: logs request method/URL and response status
    // VERBOSE: logs full headers and request/response bodies
    logLevel = InsforgeLogLevel.DEBUG

    install(Auth)
    install(Database)
    // ... other modules
}
```

| Log Level | Description                                       |
| --------- | ------------------------------------------------- |
| `NONE`    | No logging (default, recommended for production)  |
| `ERROR`   | Only errors                                       |
| `WARN`    | Warnings and errors                               |
| `INFO`    | Informational messages                            |
| `DEBUG`   | Debug info (request method, URL, response status) |
| `VERBOSE` | Full details (headers, request/response bodies)   |

<Note>
  Use `NONE` or `ERROR` in production to avoid exposing sensitive data in logs.
</Note>

### Android Initialization

1. Add Chrome Custom Tabs dependency to your `build.gradle.kts`:

```kotlin theme={null}
dependencies {
    implementation("androidx.browser:browser:1.9.0")
}
```

2. Initialize InsForge SDK (With Chrome Custom Tabs and Session Storage)

```kotlin theme={null}
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.ClientType
import dev.insforge.auth.SessionStorage
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage

class InsforgeManager(private val context: Context) {

    val client = createInsforgeClient(
        baseUrl = "https://your-app.insforge.app",
        anonKey = "your-anon-key"
    ) {
        install(Auth) {
            // 1. Chrome Custom Tabs - opens in-app, similar to iOS ASWebAuthenticationSession
            browserLauncher = BrowserLauncher { url ->
                val customTabsIntent = CustomTabsIntent.Builder()
                    .setShowTitle(true)
                    .build()
                // Handle non-Activity context safely
                if (context is Activity) {
                    customTabsIntent.launchUrl(context, Uri.parse(url))
                } else {
                    customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    customTabsIntent.launchUrl(context, Uri.parse(url))
                }
            }

            // 2. enable session persistence
            persistSession = true

            // 3. config SessionStorage (use SharedPreferences)
            sessionStorage = object : SessionStorage {
                private val prefs = context.getSharedPreferences(
                    "insforge_auth",
                    Context.MODE_PRIVATE
                )

                override suspend fun save(key: String, value: String) {
                    prefs.edit().putString(key, value).apply()
                }

                override suspend fun get(key: String): String? {
                    return prefs.getString(key, null)
                }

                override suspend fun remove(key: String) {
                    prefs.edit().remove(key).apply()
                }
            }

            // 4. set client type for mobile
            clientType = ClientType.MOBILE
        }
        // Install Database module
        install(Database)

        // Install Realtime module for real-time subscriptions
        install(Realtime) {
            debug = true
        }
        // Install other modules
        install(Storage)
        install(Functions)
        install(AI)
    }
}
```

3. Use Jetpack DataStore for Session Storage (Optional)

```kotlin theme={null}
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

val Context.authDataStore: DataStore<Preferences> by preferencesDataStore(name = "insforge_auth")

class DataStoreSessionStorage(private val context: Context) : SessionStorage {
    
    override suspend fun save(key: String, value: String) {
        context.authDataStore.edit { prefs ->
            prefs[stringPreferencesKey(key)] = value
        }
    }
    
    override suspend fun get(key: String): String? {
        return context.authDataStore.data.map { prefs ->
            prefs[stringPreferencesKey(key)]
        }.first()
    }
    
    override suspend fun remove(key: String) {
        context.authDataStore.edit { prefs ->
            prefs.remove(stringPreferencesKey(key))
        }
    }
}

// Then use it in your InsForge client
install(Auth) {
    browserLauncher = ...
    persistSession = true
    sessionStorage = DataStoreSessionStorage(context)
}
```

## 心智模型

Kotlin SDK 通过 Socket.IO 连接到 InsForge 实时系统。订阅频道名称（如 `order:123` 或 `chat:room-1`），然后监听发布到这些频道的事件名称。

事件由数据库触发器发布，这些触发器调用 `realtime.publish(...)` 或由已加入频道的客户端发布。有关频道和 RLS 模型，请参阅[实时概述](/core-concepts/realtime/overview)。

## 快速开始

```kotlin theme={null}
import dev.insforge.realtime.models.SocketMessage

client.realtime.connect()

val response = client.realtime.subscribe("order:$orderId")

if (!response.ok) {
    println("Subscribe failed: ${response.error?.message}")
    return
}

client.realtime.on<SocketMessage>("status_changed") { message ->
    message?.let {
        println("Status changed: ${it.payload}")
    }
}
```

## connect()

建立实时连接。

```kotlin theme={null}
client.realtime.connect()

println("Connected: ${client.realtime.isConnected}")
println("Socket ID: ${client.realtime.socketId}")
```

监视连接状态：

```kotlin theme={null}
client.realtime.connectionState.collect { state ->
    when (state) {
        is Realtime.ConnectionState.Connected -> println("Connected")
        is Realtime.ConnectionState.Connecting -> println("Connecting")
        is Realtime.ConnectionState.Disconnected -> println("Disconnected")
        is Realtime.ConnectionState.Error -> println("Error: ${state.message}")
    }
}
```

## subscribe()

订阅频道并接收当前在线状态快照。

```kotlin theme={null}
val response = client.realtime.subscribe("chat:room-1")

if (response.ok) {
    println("Subscribed to: ${response.channel}")
    println("Members: ${response.presence?.members}")
} else {
    println("Error: ${response.error?.message}")
}
```

## publish()

将事件发布到频道。

```kotlin theme={null}
client.realtime.publish(
    channel = "chat:room-1",
    event = "new_message",
    payload = mapOf(
        "text" to "Hello from Kotlin",
        "sentAt" to System.currentTimeMillis()
    )
)
```

<Note>
  客户端必须订阅频道才能向该频道发布。
</Note>

如果在 `realtime.messages` 上启用了 RLS，发布也会根据 `INSERT` 策略进行检查。

## on()

注册事件监听器。

```kotlin theme={null}
client.realtime.on<SocketMessage>("new_message") { message ->
    message?.let {
        println("Message: ${it.payload}")
    }
}
```

连接和系统事件：

| 事件               | 说明            |
| ---------------- | ------------- |
| `presence:join`  | 逻辑成员在频道中变为在线。 |
| `presence:leave` | 逻辑成员不再在频道中在线。 |
| `realtime:error` | 订阅或发布失败。      |

## once()

仅监听一次事件。

```kotlin theme={null}
client.realtime.once<SocketMessage>("checkout_completed") { message ->
    println("Checkout completed: ${message?.payload}")
}
```

## off()

移除事件监听器。

```kotlin theme={null}
val callback = Realtime.EventCallback<SocketMessage> { message ->
    println("Event received: $message")
}

client.realtime.on("status_changed", callback)
client.realtime.off("status_changed", callback)
```

## unsubscribe()

离开频道。

```kotlin theme={null}
client.realtime.unsubscribe("chat:room-1")
```

## disconnect()

关闭实时连接。

```kotlin theme={null}
client.realtime.disconnect()
```

## getSubscribedChannels()

返回本地订阅的频道。

```kotlin theme={null}
val channels = client.realtime.getSubscribedChannels()
println(channels)
```

## 在线状态

成功的订阅会返回在线状态快照。监听 `presence:join` 和 `presence:leave` 以更新本地在线状态。

在线状态是临时的。它不是一个持久的房间成员身份表。
