rivetkit-client-swift
RivetKit Swift client guidance. Use for Swift clients that connect to Rivet Actors with RivetKitClient, create actor handles, call actions, or manage connections.
What this skill does
# RivetKit Swift Client
Use this skill when building Swift clients that connect to Rivet Actors with `RivetKitClient`.
## Version
RivetKit version: 2.3.0-rc.5
## Error Handling Policy
- Prefer fail-fast behavior by default.
- Avoid broad `do/catch` unless absolutely needed.
- If a catch block is used, handle the error explicitly, at minimum by logging it.
## Install
Add the Swift package dependency and import `RivetKitClient`:
```swift
// 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
```swift
import RivetKitClient
let config = try ClientConfig(
endpoint: "https://my-namespace:[email protected]"
)
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)
```
## Stateless vs Stateful
```swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:6420")
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
```swift
import RivetKitClient
struct GameInput: Encodable {
let mode: String
}
let config = try ClientConfig(endpoint: "http://localhost:6420")
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:6420")
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:6420")
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
```swift
import RivetKitClient
struct ConnParams: Encodable {
let authToken: String
}
let config = try ClientConfig(endpoint: "http://localhost:6420")
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
```swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:6420")
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`:
```swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:6420")
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:6420")
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:
```swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:6420")
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.):
```swift
import RivetKitClient
let config = try ClientConfig(endpoint: "http://localhost:6420")
let client = RivetKitClient(config: config)
let hanRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.