rivetkit-client-swift

安装量: 1.6K
排名: #961

安装

npx skills add https://github.com/rivet-dev/skills --skill rivetkit-client-swift

RivetKit Swift Client

Use this skill when building Swift clients that connect to Rivet Actors with RivetKitClient.

Version

RivetKit version: 2.0.42-rc.1

Install

Add the Swift package dependency and import 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") ] ) ]

Minimal Client Endpoint 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)

Explicit Fields 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 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()

Getting Actors 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:

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:

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()

Connection Parameters 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()

Subscribing to Events 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 JSONValue:

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 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()

Low-Level HTTP & WebSocket

For actors that implement onRequest or onWebSocket, you can call them directly:

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()

Calling from Backend

Use the same client in server-side Swift (Vapor, Hummingbird, etc.):

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()

Error Handling 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 JSONValue:

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()

Concepts Keys

Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing:

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 "org:(userId)" when userId contains user data. Use arrays instead to prevent key injection attacks.

Environment Variables

ClientConfig reads optional values from environment variables:

RIVET_NAMESPACE - Namespace (can also be in endpoint URL) RIVET_TOKEN - Authentication token (can also be in endpoint URL) RIVET_RUNNER - Runner name (defaults to "default")

The endpoint parameter is always required. There is no default endpoint.

Endpoint Format

Endpoints support URL auth syntax:

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

You can also pass the endpoint without auth and provide RIVET_NAMESPACE and RIVET_TOKEN separately. For serverless deployments, set the endpoint to your app's /api/rivet URL. See Endpoints for details.

API Reference Client RivetKitClient(config:) - Create a client with a config ClientConfig - Configure endpoint, namespace, and token client.get() / getOrCreate() / getForId() / create() - Get actor handles client.dispose() - Dispose the client and all connections ActorHandle handle.action(name, args..., as:) - Stateless action call handle.connect() - Create a stateful connection handle.resolve() - Get the actor ID handle.getGatewayUrl() - Get the raw gateway URL handle.fetch(path, request:) - Raw HTTP request handle.websocket(path:) - Raw WebSocket connection ActorConnection conn.action(name, args..., as:) - Action call over WebSocket conn.events(name, as:) - AsyncStream of typed events conn.statusChanges() - AsyncStream of status changes conn.errors() - AsyncStream of connection errors conn.opens() - AsyncStream that yields on connection open conn.closes() - AsyncStream that yields on connection close conn.currentStatus - Current connection status conn.dispose() - Close the connection Types ActorConnStatus - Connection status enum (.idle, .connecting, .connected, .disconnected, .disposed) ActorError - Typed actor errors with group, code, message, metadata JSONValue - Raw JSON value for untyped responses Need More Than the Client?

If you need more about Rivet Actors, registries, or server-side RivetKit, add the main skill:

npx skills add rivet-dev/skills

Then use the rivetkit skill for backend guidance.

返回排行榜