rivetkit-client-swiftui について
`RivetKitSwiftUI` で Rivet Actors に接続する SwiftUI アプリ向けのスキルです。`@Actor` プロパティラッパー、アクション呼び出し、イベントストリーム、接続状態、クライアント設定を解説します。
主な機能
- 01`@Actor` ラッパーによる Actor 接続
- 02非同期またはファイア・アンド・フォーゲットのアクション呼び出し
- 03型付きイベントとストリームの購読
- 04接続状態とエラーの確認
- 05ビュー階層でのクライアント設定
活用シーン
- 01Rivet Actors を利用する SwiftUI ビューの構築
- 02アプリの UI から Actor アクションを呼び出す
- 03Actor の接続状態表示とエラー処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rivet-dev/ skills --skill rivetkit-client-swiftuiインストール後、Claude Code で/rivetkit-client-swiftuiを入力して読み込まれたか確認します。
同梱ファイルindex.json
詳細
RivetKit SwiftUI クライアント
RivetKitSwiftUI を使って Rivet Actors に接続する 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 を再エクスポートするため、1 つのインポートで両方を利用できます。
最小限のクライアント
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 プロパティラッパーは常に取得または作成のセマンティクスを使用し、次の項目を受け取ります。
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)"
}
}
}
概念
キー
キーはアクターインスタンスを一意に識別します。階層的なアドレス指定には、複合キー(配列)を使用してください。
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:) を使用すると、クライアントは一度だけ作成され、エンドポイントごとにキャッシュされます。.rivetKit(client:) を使用する場合は、SwiftUI が body を複数回呼び出すことがあるため、クライアントを body の内部ではなく App のプロパティとして保持してください。
環境変数
ClientConfig は、環境変数から省略可能な値を読み取ります。
RIVET_NAMESPACE- ネームスペース(エンドポイント URL にも指定できます)RIVET_TOKEN- 認証トークン(エンドポイント URL にも指定できます)RIVET_RUNNER- Runner 名(デフォルトは"default")
エンドポイントは常に必須です。デフォルトのエンドポイントはありません。
エンドポイントの形式
エンドポイントは URL 認証構文に対応しています。
https://namespace:[email protected]
認証情報を含めずにエンドポイントを渡し、RIVET_NAMESPACE と RIVET_TOKEN を個別に指定することもできます。サーバーレス環境にデプロイする場合は、エンドポイントにアプリの /api/rivet URL を設定してください。詳細については、エンドポイントを参照してください。
API リファレンス
プロパティラッパー
@Actor(name, key:, params:, createWithInput:, createInRegion:, enabled:)- アクター接続用の SwiftUI プロパティラッパー
ビューモディファイア
.rivetKit(endpoint:)- エンドポイント URL を指定してクライアントを設定します(キャッシュされたクライアントを作成).rivetKit(client:)- カスタムインスタンスを指定してクライアントを設定します.onActorEvent(actor, event) { ... }- アクターイベントを購読します(型付き引数 0~5 個に対応).onActorError(actor) { error in ... }- アクターエラーを処理します
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-group、code、message、metadataを持つ型付きアクターエラー
クライアント以外の情報が必要ですか?
Rivet Actors、レジストリ、またはサーバー側の RivetKit について詳しく知りたい場合は、メインスキルを追加してください。
npx skills add rivet-dev/skills
その後、バックエンドのガイダンスには rivetkit スキルを使用してください。