rivetkit-client-swift

rivetkit-client-swift

RivetKit Swift 客戶端指南。用於使用 RivetKitClient 連接到 Rivet Actors、建立 Actor 控制代碼、呼叫動作或管理連線的 Swift 客戶端。

16星標
7分支
更新於 2026/7/15
SKILL.md
readonlyread-only
name
rivetkit-client-swift
description

RivetKit Swift 客戶端指南。用於使用 RivetKitClient 連接到 Rivet Actors、建立 Actor 控制代碼、呼叫動作或管理連線的 Swift 客戶端。

RivetKit Swift 客戶端

當您建立連接到 Rivet Actors 的 Swift 客戶端時,請使用此技能,搭配 RivetKitClient

版本

RivetKit 版本:2.3.4

錯誤處理原則

  • 預設偏好快速失敗(fail-fast)行為。
  • 除非絕對必要,否則避免使用寬泛的 do/catch
  • 如果使用了 catch 區塊,請明確處理錯誤,至少記錄下來。

安裝

加入 Swift 套件依賴並匯入 RivetKitClient

// Package.swift
dependencies: [
    .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0")
]

targets: [
    .target(
        name: "MyApp",
        dependencies: [
            .product(name: "RivetKitClient", package: "rivetkit-swift")
        ]
    )
]

最小客戶端

端點 URL

import RivetKitClient

let config = try ClientConfig(
    endpoint: "https://my-namespace:pk_...@api.rivet.dev"
)
let client = RivetKitClient(config: config)

let handle = client.getOrCreate("counter", ["my-counter"])
let count: Int = try await handle.action("increment", 1, as: Int.self)

明確欄位

import RivetKitClient

let config = try ClientConfig(
    endpoint: "https://api.rivet.dev",
    namespace: "my-namespace",
    token: "pk_..."
)
let client = RivetKitClient(config: config)

let handle = client.getOrCreate("counter", ["my-counter"])
let count: Int = try await handle.action("increment", 1, as: Int.self)

無狀態 vs 有狀態

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

let handle = client.getOrCreate("counter", ["my-counter"])

// 無狀態:每次呼叫獨立
let current: Int = try await handle.action("getCount", as: Int.self)
print("目前計數:\(current)")

// 有狀態:保持連線以接收即時事件
let conn = handle.connect()

// 使用 AsyncStream 訂閱事件
let eventTask = Task {
    for await count in await conn.events("count", as: Int.self) {
        print("事件:\(count)")
    }
}

_ = try await conn.action("increment", 1, as: Int.self)

eventTask.cancel()
await conn.dispose()
await client.dispose()

取得 Actors

import RivetKitClient

struct GameInput: Encodable {
    let mode: String
}

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

// 取得或建立一個 actor
let room = client.getOrCreate("chatRoom", ["room-42"])

// 取得現有 actor(若找不到則失敗)
let existing = client.get("chatRoom", ["room-42"])

// 建立新 actor 並帶入輸入
let created = try await client.create(
    "game",
    ["game-1"],
    options: CreateOptions(input: GameInput(mode: "ranked"))
)

// 根據 ID 取得 actor
let byId = client.getForId("chatRoom", "actor-id")

// 解析 actor ID
let resolvedId = try await room.resolve()
print("解析後的 ID:\(resolvedId)")

await client.dispose()

動作支援 0 到 5 個參數的位置多載:

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("counter", ["my-counter"])

let count: Int = try await handle.action("getCount")
let updated: String = try await handle.action("rename", "new-name")
let ok: Bool = try await handle.action("setScore", "user-1", 42)

print("計數:\(count),更新後:\(updated),結果:\(ok)")
await client.dispose()

如果需要超過 5 個參數,請使用原始 JSON 備援:

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("counter", ["my-counter"])

let args: [JSONValue] = [
    .string("user-1"),
    .number(.int(42)),
    .string("extra"),
    .string("more"),
    .string("args"),
    .string("here")
]
let ok: Bool = try await handle.action("setScore", args: args, as: Bool.self)
print("結果:\(ok)")

await client.dispose()

連線參數

import RivetKitClient

struct ConnParams: Encodable {
    let authToken: String
}

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

let chat = client.getOrCreate(
    "chatRoom",
    ["general"],
    options: GetOrCreateOptions(params: ConnParams(authToken: "jwt-token-here"))
)

let conn = chat.connect()

// 使用連線...
for await status in await conn.statusChanges() {
    print("狀態:\(status.rawValue)")
    if status == .connected {
        break
    }
}

await conn.dispose()
await client.dispose()

訂閱事件

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let conn = client.getOrCreate("chatRoom", ["general"]).connect()

// 使用 AsyncStream 訂閱事件
let messageTask = Task {
    for await (from, body) in await conn.events("message", as: (String, String).self) {
        print("\(from):\(body)")
    }
}

// 一次性事件,收到後中斷
let gameOverTask = Task {
    for await _ in await conn.events("gameOver", as: Void.self) {
        print("完成")
        break
    }
}

// 讓它跑一段時間
try await Task.sleep(for: .seconds(5))

// 完成時取消
messageTask.cancel()
gameOverTask.cancel()
await conn.dispose()
await client.dispose()

事件串流支援 0 到 5 個型別參數。如果需要原始值或超過 5 個參數,請使用 JSONValue

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let conn = client.getOrCreate("chatRoom", ["general"]).connect()

let rawTask = Task {
    for await args in await conn.events("message") {
        print(args)
    }
}

try await Task.sleep(for: .seconds(5))
rawTask.cancel()
await conn.dispose()
await client.dispose()

連線生命週期

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let conn = client.getOrCreate("chatRoom", ["general"]).connect()

// 監控狀態變化(立即產生目前狀態)
let statusTask = Task {
    for await status in await conn.statusChanges() {
        print("狀態:\(status.rawValue)")
    }
}

// 監控錯誤
let errorTask = Task {
    for await error in await conn.errors() {
        print("錯誤:\(error.group).\(error.code)")
    }
}

// 監控開啟/關閉事件
let openTask = Task {
    for await _ in await conn.opens() {
        print("已連線")
    }
}

let closeTask = Task {
    for await _ in await conn.closes() {
        print("已斷線")
    }
}

// 檢查目前狀態
let current = await conn.currentStatus
print("目前狀態:\(current.rawValue)")

// 讓它跑一段時間
try await Task.sleep(for: .seconds(5))

// 清理
statusTask.cancel()
errorTask.cancel()
openTask.cancel()
closeTask.cancel()
await conn.dispose()
await client.dispose()

低階 HTTP 與 WebSocket

對於實作 onRequestonWebSocket 的 actors,可以直接呼叫:

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("chatRoom", ["general"])

// 原始 HTTP 請求
let response = try await handle.fetch("history")
let history: [String] = try response.json([String].self)
print("歷史記錄:\(history)")

// 原始 WebSocket 連線
let websocket = try await handle.websocket(path: "stream")
try await websocket.send(text: "hello")
let message = try await websocket.receive()
print("收到:\(message)")

await client.dispose()

從後端呼叫

在伺服器端 Swift(Vapor、Hummingbird 等)中使用相同的客戶端:

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

let handle = client.getOrCreate("counter", ["server-counter"])
let count: Int = try await handle.action("increment", 1, as: Int.self)
print("計數:\(count)")

await client.dispose()

錯誤處理

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

do {
    _ = try await client.getOrCreate("user", ["user-123"])
        .action("updateUsername", "ab", as: String.self)
} catch let error as ActorError {
    print("錯誤代碼:\(error.code)")
    print("中繼資料:\(String(describing: error.metadata))")
}

await client.dispose()

如果需要未型別的回應,可以解碼為 JSONValue

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("data", ["raw"])

let value: JSONValue = try await handle.action("getRawPayload")
print("原始值:\(value)")

await client.dispose()

概念

鍵值

鍵值唯一識別 actor 實例。使用複合鍵值(陣列)進行階層式定址:

import RivetKitClient

let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)

// 使用複合鍵值進行階層式定址
let room = client.getOrCreate("chatRoom", ["org-acme", "general"])
let actorId = try await room.resolve()
print("Actor ID:\(actorId)")

await client.dispose()

不要使用字串插值(如 "org:\(userId)")來建立鍵值,因為 userId 可能包含使用者資料。請改用陣列以防止鍵值注入攻擊。

環境變數

ClientConfig 會從環境變數讀取選用值:

  • RIVET_NAMESPACE - 命名空間(也可放在端點 URL 中)
  • RIVET_TOKEN - 驗證令牌(也可放在端點 URL 中)
  • RIVET_RUNNER - Runner 名稱(預設為 "default"

endpoint 參數永遠是必要的。沒有預設端點。

端點格式

端點支援 URL 驗證語法:

https://namespace:token@api.rivet.dev

您也可以傳入不含驗證資訊的端點,並分別提供 RIVET_NAMESPACERIVET_TOKEN。對於無伺服器部署,請將端點設為您應用程式的 /api/rivet URL。詳情請參閱端點

API 參考

客戶端

  • RivetKitClient(config:) - 使用設定建立客戶端
  • ClientConfig - 設定端點、命名空間和令牌
  • client.get() / getOrCreate() / getForId() / create() - 取得 actor 控制代碼
  • client.dispose() - 釋放客戶端及所有連線

ActorHandle

  • handle.action(name, args..., as:) - 無狀態動作呼叫
  • handle.connect() - 建立有狀態連線
  • handle.resolve() - 取得 actor ID
  • handle.getGatewayUrl() - 取得原始閘道 URL
  • handle.fetch(path, request:) - 原始 HTTP 請求
  • handle.websocket(path:) - 原始 WebSocket 連線

ActorConnection

  • conn.action(name, args..., as:) - 透過 WebSocket 呼叫動作
  • conn.events(name, as:) - 型別事件的 AsyncStream
  • conn.statusChanges() - 狀態變化的 AsyncStream
  • conn.errors() - 連線錯誤的 AsyncStream
  • conn.opens() - 連線開啟時產生事件的 AsyncStream
  • conn.closes() - 連線關閉時產生事件的 AsyncStream
  • conn.currentStatus - 目前連線狀態
  • conn.dispose() - 關閉連線

型別

  • ActorConnStatus - 連線狀態列舉(.idle.connecting.connected.disconnected.disposed
  • ActorError - 型別化的 actor 錯誤,包含 groupcodemessagemetadata
  • JSONValue - 用於未型別回應的原始 JSON 值

需要更多客戶端功能?

如果您需要更多關於 Rivet Actors、註冊表或伺服器端 RivetKit 的資訊,請加入主要技能:

npx skills add rivet-dev/skills

然後使用 rivetkit 技能取得後端指引。