Skip to content
Mobile
Skill

/tabletopkit

Builds multiplayer spatial board games using TabletopKit on visionOS. Use when creating tabletop game experiences with boards, pieces, cards, or dice; managing seats, turns, equipment state, TabletopAction flows, or TabletopInteraction delegates; synchronizing gameplay through

From plugin
swift-ios-skills
1.3k86 skills1 MCP

Claude Code

$ npx -y skills add dpearson2699/swift-ios-skills --skill tabletopkit --agent claude-code

SKILL.md

tabletopkit.SKILL.md
name: tabletopkit
description: "Builds multiplayer spatial board games using TabletopKit on visionOS. Use when creating tabletop game experiences with boards, pieces, cards, or dice; managing seats, turns, equipment state, TabletopAction flows, or TabletopInteraction delegates; synchronizing gameplay through FaceTime Group Activities; rendering with RealityKit; or implementing snapping, tosses, and physics on a virtual table surface."

TabletopKit

Build visionOS board games whose synchronized state changes flow through `TabletopAction` and render with RealityKit. The availability matrix below owns version details.

Contents

  • [Setup](#setup)
  • [Game Configuration](#game-configuration)
  • [Table and Board](#table-and-board)
  • [Equipment (Pieces, Cards, Dice)](#equipment-pieces-cards-dice)
  • [Player Seats](#player-seats)
  • [Game Actions and Turns](#game-actions-and-turns)
  • [Interactions](#interactions)
  • [RealityKit Rendering](#realitykit-rendering)
  • [Group Activities Integration](#group-activities-integration)
  • [Common Mistakes](#common-mistakes)
  • [Review Checklist](#review-checklist)
  • [References](#references)

Setup

| Tier | APIs | |---|---| | visionOS 2.0+ | Core gameplay, equipment, seats, actions, rendering, Group Activities | | visionOS 2.2+ | `TabletopInteraction.Configuration` | | visionOS 26.0+ | Custom actions/state, registration, advanced toss outcomes, discarded-action observation |

Simulator supports single-player layout testing, not multiplayer.

Project Configuration

1. `import TabletopKit` in source files that define game logic. 2. `import RealityKit` for entity-based rendering. 3. For multiplayer, add the **Group Activities** capability in Signing & Capabilities. 4. Provide table, piece, card, and dice USDZ assets in a RealityKit content bundle.

Key Types Overview

| Type | Role | |---|---| | `TabletopGame` | Central game manager; owns setup, actions, observers, rendering | | `TableSetup` | Configuration object passed to `TabletopGame` init | | `Tabletop` / `EntityTabletop` | Protocol for the table surface | | `Equipment` / `EntityEquipment` | Protocol for interactive game pieces | | `TableSeat` / `EntityTableSeat` | Protocol for player seat positions | | `TabletopAction` | Commands that modify game state | | `TabletopInteraction` | Gesture-driven player interactions with equipment | | `TabletopGame.Observer` | Callback protocol for reacting to confirmed actions | | `TabletopGame.RenderDelegate` | Callback protocol for visual updates | | `EntityRenderDelegate` | RealityKit-specific render delegate |

Game Configuration

Build and validate a game in this order:

1. Define the tabletop, equipment, and seats. 2. Configure `TableSetup` and register every custom action type. 3. Create the game, attach its observer and renderer, claim a seat, and establish automatic or manual update handling. 4. Inspect the current snapshot for required equipment IDs, parents, seats, and counters before starting multiplayer. Fix the setup and rebuild if an invariant fails.

import TabletopKit
import RealityKit

let table = GameTable()
var setup = TableSetup(tabletop: table)
setup.add(seat: PlayerSeat(index: 0, pose: seatPose0))
setup.add(seat: PlayerSeat(index: 1, pose: seatPose1))
setup.add(equipment: GamePawn(id: .init(1)))
setup.add(equipment: GameDie(id: .init(2)))

let game = TabletopGame(tableSetup: setup)
game.claimAnySeat()

Call `update(deltaTime:)` each frame if automatic updates are not enabled via the `.tabletopGame(_:parent:automaticUpdate:)` modifier. Read state safely with `withCurrentSnapshot(_:)`.

Table and Board

Tabletop Protocol

Conform to `EntityTabletop` to define the playing surface. Provide a `shape` (round or rectangular) and a RealityKit `Entity` for visual representation.

struct GameTable: EntityTabletop {
    var shape: TabletopShape
    var entity: Entity
    var id: EquipmentIdentifier

    init() {
        entity = try! Entity.load(named: "table/game_table", in: contentBundle)
        shape = .round(entity: entity)
        id = .init(0)
    }
}

Table Shapes

Use factory methods on `TabletopShape`:

// Round table from dimensions
let round = TabletopShape.round(
    center: .init(x: 0, y: 0, z: 0),
    radius: 0.5,
    thickness: 0.05,
    in: .meters
)

// Rectangular table from entity
let rect = TabletopShape.rectangular(entity: tableEntity)

Equipment (Pieces, Cards, Dice)

Equipment Protocol

All interactive game objects conform to `Equipment` (or `EntityEquipment` for RealityKit-rendered pieces). Each piece has an `id` (`EquipmentIdentifier`) and an `initialState` property.

Choose the state type based on the equipment:

| State Type | Use Case | |---|---| | `BaseEquipmentState` | Generic pieces, pawns, tokens | | `CardState` | Playing cards (tracks `faceUp` / face-down) | | `DieState` | Dice with an integer `value` | | `RawValueState` | Custom data encoded as `UInt64` | | `CustomEquipmentState` | Custom state with a `BaseEquipmentState` plus game data; see the availability matrix |

Defining Equipment

// Pawn -- uses BaseEquipmentState
struct GamePawn: EntityEquipment {
    var id: EquipmentIdentifier
    var initialState: BaseEquipmentState
    var entity: Entity

    init(id: EquipmentIdentifier) {
        self.id = id
        self.entity = try! Entity.load(named: "pieces/pawn", in: contentBundle)
        self.initialState = BaseEquipmentState(
            parentID: .init(0), seatControl: .any,
            pose: .identity, entity: entity
        )
    }
}

// Card -- uses CardState (tracks faceUp)
struct PlayingCard: EntityEquipment {
    var id: EquipmentIdentifier
    var initialState: CardState
    var entity: Entity

    init(id: EquipmentIdentifier) {
        self.id = id
        self.entity = try! Entity.load(named: "cards/card", in: contentBundle)
        self.initialState = .faceDown(
            parentID: .init(0), seatControl: .
Read more
@dpearson2699
Ships withswift-ios-skills

86 agent skills optimized for iOS 26+ development with Swift 6.3 and modern Apple frameworks.

1.3k86 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
1.3k
Star history of dpearson2699/swift-ios-skills on GitHub, a chart by star-history.com

Other skills on swift-ios-skills.

alarmkit

alarmkit

Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple…

app-clips

app-clips

Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store…

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo1d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens2w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about tabletopkit.

Which coding agents can use tabletopkit?
tabletopkit installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.