mcpField
R
rivetkit-client-swiftui

rivetkit-client-swiftui

github.com/rivet-dev開発447.3k 回インストール1 日前に更新
リポジトリ

rivetkit-client-swiftui について

`RivetKitSwiftUI` で Rivet Actors に接続する SwiftUI アプリ向けのスキルです。`@Actor` プロパティラッパー、アクション呼び出し、イベントストリーム、接続状態、クライアント設定を解説します。

主な機能

  1. 01`@Actor` ラッパーによる Actor 接続
  2. 02非同期またはファイア・アンド・フォーゲットのアクション呼び出し
  3. 03型付きイベントとストリームの購読
  4. 04接続状態とエラーの確認
  5. 05ビュー階層でのクライアント設定

活用シーン

  1. 01Rivet Actors を利用する SwiftUI ビューの構築
  2. 02アプリの UI から Actor アクションを呼び出す
  3. 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:) - 型付きイベントの AsyncStream
  • actor.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 スキルを使用してください。

コメント