rivetkit-client-swift
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRivetKit Swift Client
RivetKit Swift 客户端
Use this skill when building Swift clients that connect to Rivet Actors with .
RivetKitClient当你构建通过 连接到 Rivet Actors 的Swift客户端时,可参考本指南。
RivetKitClientVersion
版本
RivetKit version: 2.0.42
RivetKit 版本:2.0.42
Install
安装
Add the Swift package dependency and import :
RivetKitClientswift
// 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")
]
)
]添加Swift包依赖并导入 :
RivetKitClientswift
// 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")
]
)
]Minimal Client
最简客户端示例
Endpoint URL
端点URL
swift
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)swift
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)Explicit Fields
显式字段配置
swift
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)swift
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)Stateless vs Stateful
无状态与有状态连接
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("counter", ["my-counter"])
// Stateless: each call is independent
let current: Int = try await handle.action("getCount", as: Int.self)
print("Current count: \(current)")
// Stateful: keep a connection open for realtime events
let conn = handle.connect()
// Subscribe to events using AsyncStream
let eventTask = Task {
for await count in await conn.events("count", as: Int.self) {
print("Event: \(count)")
}
}
_ = try await conn.action("increment", 1, as: Int.self)
eventTask.cancel()
await conn.dispose()
await client.dispose()swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Getting Actors
获取Actor实例
swift
import RivetKitClient
struct GameInput: Encodable {
let mode: String
}
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
// Get or create an actor
let room = client.getOrCreate("chatRoom", ["room-42"])
// Get an existing actor (fails if not found)
let existing = client.get("chatRoom", ["room-42"])
// Create a new actor with input
let created = try await client.create(
"game",
["game-1"],
options: CreateOptions(input: GameInput(mode: "ranked"))
)
// Get actor by ID
let byId = client.getForId("chatRoom", "actor-id")
// Resolve actor ID
let resolvedId = try await room.resolve()
print("Resolved ID: \(resolvedId)")
await client.dispose()Actions support positional overloads for 0–5 args:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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: \(count), Updated: \(updated), OK: \(ok)")
await client.dispose()If you need more than 5 arguments, use the raw JSON fallback:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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: \(ok)")
await client.dispose()swift
import RivetKitClient
struct GameInput: Encodable {
let mode: String
}
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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个参数的重载:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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作为回退方案:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Connection Parameters
连接参数
swift
import RivetKitClient
struct ConnParams: Encodable {
let authToken: String
}
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let chat = client.getOrCreate(
"chatRoom",
["general"],
options: GetOrCreateOptions(params: ConnParams(authToken: "jwt-token-here"))
)
let conn = chat.connect()
// Use the connection...
for await status in await conn.statusChanges() {
print("Status: \(status.rawValue)")
if status == .connected {
break
}
}
await conn.dispose()
await client.dispose()swift
import RivetKitClient
struct ConnParams: Encodable {
let authToken: String
}
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Subscribing to Events
订阅事件
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let conn = client.getOrCreate("chatRoom", ["general"]).connect()
// Subscribe to events using AsyncStream
let messageTask = Task {
for await (from, body) in await conn.events("message", as: (String, String).self) {
print("\(from): \(body)")
}
}
// For one-time events, break after receiving
let gameOverTask = Task {
for await _ in await conn.events("gameOver", as: Void.self) {
print("done")
break
}
}
// Let it run for a bit
try await Task.sleep(for: .seconds(5))
// Cancel when done
messageTask.cancel()
gameOverTask.cancel()
await conn.dispose()
await client.dispose()Event streams support 0–5 typed arguments. If you need raw values or more than 5 arguments, use :
JSONValueswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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个参数,可使用:
JSONValueswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Connection Lifecycle
连接生命周期
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let conn = client.getOrCreate("chatRoom", ["general"]).connect()
// Monitor status changes (immediately yields current status)
let statusTask = Task {
for await status in await conn.statusChanges() {
print("status: \(status.rawValue)")
}
}
// Monitor errors
let errorTask = Task {
for await error in await conn.errors() {
print("error: \(error.group).\(error.code)")
}
}
// Monitor open/close events
let openTask = Task {
for await _ in await conn.opens() {
print("connected")
}
}
let closeTask = Task {
for await _ in await conn.closes() {
print("disconnected")
}
}
// Check current status
let current = await conn.currentStatus
print("Current status: \(current.rawValue)")
// Let it run for a bit
try await Task.sleep(for: .seconds(5))
// Cleanup
statusTask.cancel()
errorTask.cancel()
openTask.cancel()
closeTask.cancel()
await conn.dispose()
await client.dispose()swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Low-Level HTTP & WebSocket
底层HTTP与WebSocket
For actors that implement or , you can call them directly:
onRequestonWebSocketswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("chatRoom", ["general"])
// Raw HTTP request
let response = try await handle.fetch("history")
let history: [String] = try response.json([String].self)
print("History: \(history)")
// Raw WebSocket connection
let websocket = try await handle.websocket(path: "stream")
try await websocket.send(text: "hello")
let message = try await websocket.receive()
print("Received: \(message)")
await client.dispose()对于实现了或的actor,你可以直接调用它们:
onRequestonWebSocketswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Calling from Backend
在后端中调用
Use the same client in server-side Swift (Vapor, Hummingbird, etc.):
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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: \(count)")
await client.dispose()在服务器端Swift(Vapor、Hummingbird等)中可使用同一个客户端:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()Error Handling
错误处理
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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: \(error.code)")
print("Metadata: \(String(describing: error.metadata))")
}
await client.dispose()If you need an untyped response, you can decode to :
JSONValueswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("data", ["raw"])
let value: JSONValue = try await handle.action("getRawPayload")
print("Raw value: \(value)")
await client.dispose()swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()如果需要非类型化响应,可解码为:
JSONValueswift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
let handle = client.getOrCreate("data", ["raw"])
let value: JSONValue = try await handle.action("getRawPayload")
print("原始值:\(value)")
await client.dispose()Concepts
核心概念
Keys
键(Keys)
Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
let client = RivetKitClient(config: config)
// Use compound keys for hierarchical addressing
let room = client.getOrCreate("chatRoom", ["org-acme", "general"])
let actorId = try await room.resolve()
print("Actor ID: \(actorId)")
await client.dispose()Don't build keys with string interpolation like when contains user data. Use arrays instead to prevent key injection attacks.
"org:\(userId)"userId键用于唯一标识actor实例。可使用复合键(数组)进行层级寻址:
swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:3000/api/rivet")
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()当包含用户数据时,不要使用字符串拼接(如)来构建键,应使用数组以避免键注入攻击。
userId"org:\(userId)"Environment Variables
环境变量
ClientConfig- - Namespace (can also be in endpoint URL)
RIVET_NAMESPACE - - Authentication token (can also be in endpoint URL)
RIVET_TOKEN - - Runner name (defaults to
RIVET_RUNNER)"default"
The parameter is always required. There is no default endpoint.
endpointClientConfig- - 命名空间(也可包含在端点URL中)
RIVET_NAMESPACE - - 认证令牌(也可包含在端点URL中)
RIVET_TOKEN - - 运行器名称(默认值为
RIVET_RUNNER)"default"
endpointEndpoint Format
端点格式
Endpoints support URL auth syntax:
https://namespace:token@api.rivet.devYou can also pass the endpoint without auth and provide and separately. For serverless deployments, set the endpoint to your app's URL. See Endpoints for details.
RIVET_NAMESPACERIVET_TOKEN/api/rivet端点支持URL认证语法:
https://namespace:token@api.rivet.devAPI Reference
API参考
Client
客户端(Client)
- - Create a client with a config
RivetKitClient(config:) - - Configure endpoint, namespace, and token
ClientConfig - /
client.get()/getOrCreate()/getForId()- Get actor handlescreate() - - Dispose the client and all connections
client.dispose()
- - 使用配置创建客户端
RivetKitClient(config:) - - 配置端点、命名空间和令牌
ClientConfig - /
client.get()/getOrCreate()/getForId()- 获取actor句柄create() - - 销毁客户端及所有连接
client.dispose()
ActorHandle
Actor句柄(ActorHandle)
- - Stateless action call
handle.action(name, args..., as:) - - Create a stateful connection
handle.connect() - - Get the actor ID
handle.resolve() - - Get the raw gateway URL
handle.getGatewayUrl() - - Raw HTTP request
handle.fetch(path, request:) - - Raw WebSocket connection
handle.websocket(path:)
- - 无状态操作调用
handle.action(name, args..., as:) - - 创建有状态连接
handle.connect() - - 获取actor ID
handle.resolve() - - 获取原始网关URL
handle.getGatewayUrl() - - 原始HTTP请求
handle.fetch(path, request:) - - 原始WebSocket连接
handle.websocket(path:)
ActorConnection
Actor连接(ActorConnection)
- - Action call over WebSocket
conn.action(name, args..., as:) - - AsyncStream of typed events
conn.events(name, as:) - - AsyncStream of status changes
conn.statusChanges() - - AsyncStream of connection errors
conn.errors() - - AsyncStream that yields on connection open
conn.opens() - - AsyncStream that yields on connection close
conn.closes() - - Current connection status
conn.currentStatus - - Close the connection
conn.dispose()
- - 通过WebSocket调用操作
conn.action(name, args..., as:) - - 类型化事件的AsyncStream
conn.events(name, as:) - - 状态变化的AsyncStream
conn.statusChanges() - - 连接错误的AsyncStream
conn.errors() - - 连接开启事件的AsyncStream
conn.opens() - - 连接关闭事件的AsyncStream
conn.closes() - - 当前连接状态
conn.currentStatus - - 关闭连接
conn.dispose()
Types
类型定义
- - Connection status enum (
ActorConnStatus,.idle,.connecting,.connected,.disconnected).disposed - - Typed actor errors with
ActorError,group,code,messagemetadata - - Raw JSON value for untyped responses
JSONValue
- - 连接状态枚举(
ActorConnStatus,.idle,.connecting,.connected,.disconnected).disposed - - 带
ActorError、group、code、message的类型化actor错误metadata - - 用于非类型化响应的原始JSON值
JSONValue
Need More Than the Client?
需要更多功能?
If you need more about Rivet Actors, registries, or server-side RivetKit, add the main skill:
bash
npx skills add rivet-dev/skillsThen use the skill for backend guidance.
rivetkit如果你需要了解更多关于Rivet Actors、注册中心或服务器端RivetKit的内容,可添加主技能包:
bash
npx skills add rivet-dev/skills然后使用技能获取后端相关指导。
rivetkit