关于 rivetkit-client-swiftui
面向 SwiftUI 应用开发者的技能,介绍如何通过 `RivetKitSwiftUI` 连接 Rivet Actors。内容涵盖 `@Actor` 属性包装器、动作调用、事件流、连接状态及客户端配置。
主要功能
- 01通过 `@Actor` 包装器连接 Actor
- 02异步或即发即弃地调用动作
- 03订阅类型化事件和事件流
- 04查看连接状态和错误
- 05在视图树中配置客户端
适用场景
- 01构建由 Rivet Actors 支持的 SwiftUI 视图
- 02从应用界面调用 Actor 动作
- 03展示 Actor 连接状态并处理错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rivet-dev/ skills --skill rivetkit-client-swiftui安装后在 Claude Code 中输入/rivetkit-client-swiftui确认已加载。
附带文件index.json
详细介绍
RivetKit SwiftUI 客户端
构建通过 RivetKitSwiftUI 连接到 Rivet Actor 的 SwiftUI 应用时,请使用此技能。
版本
RivetKit 版本:2.3.7
错误处理策略
- 默认优先采用快速失败行为。
- 除非绝对必要,否则避免使用宽泛的
do/catch。 - 如果使用
catch块,请显式处理错误,至少将其记录下来。
安装
添加 Swift 包依赖并导入 RivetKitSwiftUI:
// Package.swift
dependencies: [
.package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0")
]
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "RivetKitSwiftUI", package: "rivetkit-swift")
]
)
]
RivetKitSwiftUI 会重新导出 RivetKitClient 和 SwiftUI,因此只需导入一次即可同时使用两者。
最简客户端
import RivetKitSwiftUI
import SwiftUI
@main
struct HelloWorldApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.rivetKit(endpoint: "https://my-namespace:[email protected]")
}
}
}
import RivetKitSwiftUI
import SwiftUI
struct ContentView: View {
@Actor("counter", key: ["my-counter"]) private var counter
@State private var count = 0
var body: some View {
VStack(spacing: 16) {
Text("\(count)")
.font(.system(size: 64, weight: .bold, design: .rounded))
Button("Increment") {
counter.send("increment", 1)
}
.disabled(!counter.isConnected)
}
.task {
count = (try? await counter.action("getCount")) ?? 0
}
.onActorEvent(counter, "newCount") { (newCount: Int) in
count = newCount
}
}
}
Actor 选项
@Actor 属性包装器始终采用获取或创建语义,并接受:
name(必需)key,类型为String或[String](必需)params(可选的连接参数)createWithInput(可选的创建输入)createInRegion(可选的创建区域提示)enabled(切换连接生命周期)
import RivetKitSwiftUI
import SwiftUI
struct ConnParams: Encodable {
let authToken: String
}
struct ChatView: View {
@Actor(
"chatRoom",
key: ["general"],
params: ConnParams(authToken: "jwt-token"),
enabled: true
) private var chat
var body: some View {
Text("Chat: \(chat.connStatus.rawValue)")
}
}
操作
import RivetKitSwiftUI
import SwiftUI
struct CounterView: View {
@Actor("counter", key: ["my-counter"]) private var counter
@State private var count = 0
@State private var name = ""
var body: some View {
VStack {
Text("Count: \(count)")
Text("Name: \(name)")
Button("Fetch") {
Task {
count = try await counter.action("getCount")
name = try await counter.action("rename", "new-name")
}
}
Button("Increment") {
counter.send("increment", 1)
}
}
}
}
订阅事件
import RivetKitSwiftUI
import SwiftUI
struct GameView: View {
@Actor("game", key: ["game-1"]) private var game
@State private var count = 0
@State private var isGameOver = false
var body: some View {
VStack {
Text("Count: \(count)")
if isGameOver {
Text("Game Over!")
}
}
.onActorEvent(game, "newCount") { (newCount: Int) in
count = newCount
}
.onActorEvent(game, "gameOver") {
isGameOver = true
}
}
}
异步事件流
import RivetKitSwiftUI
import SwiftUI
struct ChatView: View {
@Actor("chatRoom", key: ["general"]) private var chat
@State private var messages: [String] = []
var body: some View {
List(messages, id: \.self) { message in
Text(message)
}
.task {
for await message in chat.events("message", as: String.self) {
messages.append(message)
}
}
}
}
连接状态
import RivetKitSwiftUI
import SwiftUI
struct StatusView: View {
@Actor("counter", key: ["my-counter"]) private var counter
@State private var count = 0
var body: some View {
VStack {
Text("Status: \(counter.connStatus.rawValue)")
if counter.connStatus == .connected {
Text("Connected!")
.foregroundStyle(.green)
}
Button("Fetch via Handle") {
Task {
if let handle = counter.handle {
count = try await handle.action("getCount", as: Int.self)
}
}
}
.disabled(!counter.isConnected)
}
}
}
错误处理
import RivetKitSwiftUI
import SwiftUI
struct UserView: View {
@Actor("user", key: ["user-123"]) private var user
@State private var errorMessage: String?
@State private var username = ""
var body: some View {
VStack {
TextField("Username", text: $username)
Button("Update Username") {
Task {
do {
let _: String = try await user.action("updateUsername", username)
} catch let error as ActorError {
errorMessage = "\(error.code): \(String(describing: error.metadata))"
}
}
}
if let errorMessage {
Text(errorMessage)
.foregroundStyle(.red)
}
}
.onActorError(user) { error in
errorMessage = "\(error.group).\(error.code): \(error.message)"
}
}
}
概念
键
键用于唯一标识 Actor 实例。使用复合键(数组)进行分层寻址:
import RivetKitSwiftUI
import SwiftUI
struct OrgChatView: View {
@Actor("chatRoom", key: ["org-acme", "general"]) private var room
var body: some View {
Text("Room: \(room.connStatus.rawValue)")
}
}
如果 userId 包含用户数据,请勿使用字符串插值(例如 "org:\(userId)")构建键。请改用数组,以防止键注入攻击。
环境配置
在视图树的根部调用一次 .rivetKit(endpoint:) 或 .rivetKit(client:):
// With endpoint string (recommended for most apps)
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.rivetKit(endpoint: "https://my-namespace:[email protected]")
}
}
}
// With custom client (for advanced configuration)
@main
struct MyApp: App {
private let client = RivetKitClient(
config: try! ClientConfig(endpoint: "https://api.rivet.dev", token: "pk_...")
)
var body: some Scene {
WindowGroup {
ContentView()
.rivetKit(client: client)
}
}
}
使用 .rivetKit(endpoint:) 时,客户端会创建一次,并按 endpoint 缓存。使用 .rivetKit(client:) 时,请将客户端作为 App 的属性存储(而不是放在 body 内),因为 SwiftUI 可能会多次调用 body。
环境变量
ClientConfig 会从环境变量读取可选值:
RIVET_NAMESPACE- 命名空间(也可放在 endpoint URL 中)RIVET_TOKEN- 身份验证令牌(也可放在 endpoint URL 中)RIVET_RUNNER- Runner 名称(默认为"default")
endpoint 始终是必需的。没有默认 endpoint。
Endpoint 格式
Endpoint 支持 URL 身份验证语法:
https://namespace:[email protected]
你也可以传入不含身份验证信息的 endpoint,并单独提供 RIVET_NAMESPACE 和 RIVET_TOKEN。对于无服务器部署,请将 endpoint 设置为应用的 /api/rivet URL。详情请参阅Endpoints。
API 参考
属性包装器
@Actor(name, key:, params:, createWithInput:, createInRegion:, enabled:)- 用于 Actor 连接的 SwiftUI 属性包装器
视图修饰符
.rivetKit(endpoint:)- 使用 endpoint URL 配置客户端(创建并缓存客户端).rivetKit(client:)- 使用自定义实例配置客户端.onActorEvent(actor, event) { ... }- 订阅 Actor 事件(支持 0–5 个类型化参数).onActorError(actor) { error in ... }- 处理 Actor 错误
ActorObservable
actor.action(name, args..., as:)- 异步调用操作actor.send(name, args...)- 发送并立即返回的操作actor.events(name, as:)- 类型化事件的 AsyncStreamactor.connStatus- 当前连接状态actor.isConnected- 是否已连接actor.handle- 底层ActorHandle(可选)actor.connection- 底层ActorConnection(可选)actor.error- 最近发生的错误(可选)
类型
ActorConnStatus- 连接状态枚举(.idle、.connecting、.connected、.disconnected、.disposed)ActorError- 类型化 Actor 错误,包含group、code、message、metadata
需要的不只是客户端?
如果你还需要了解 Rivet Actor、注册表或服务端 RivetKit,请添加主技能:
npx skills add rivet-dev/skills
然后使用 rivetkit 技能获取后端指导。