{"id":"30a9be2a-c406-4b6e-933f-59193761d950","shortId":"eY5mZM","kind":"skill","title":"tabletopkit","tagline":"Create multiplayer spatial board games using TabletopKit on visionOS. Use when building tabletop game experiences with boards, pieces, cards, and dice, managing player seats and turns, synchronizing game state over FaceTime with Group Activities, rendering game elements with Real","description":"# TabletopKit\n\nCreate multiplayer spatial board games on a virtual table surface using\nTabletopKit. Handles game layout, equipment interaction, player seating, turn\nmanagement, state synchronization, and RealityKit rendering. **visionOS 2.0+\nonly.** Targets Swift 6.3.\n\n## Contents\n\n- [Setup](#setup)\n- [Game Configuration](#game-configuration)\n- [Table and Board](#table-and-board)\n- [Equipment (Pieces, Cards, Dice)](#equipment-pieces-cards-dice)\n- [Player Seats](#player-seats)\n- [Game Actions and Turns](#game-actions-and-turns)\n- [Interactions](#interactions)\n- [RealityKit Rendering](#realitykit-rendering)\n- [Group Activities Integration](#group-activities-integration)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Setup\n\n### Platform Requirement\n\nTabletopKit is exclusive to visionOS. It requires visionOS 2.0+. Multiplayer\nfeatures using Group Activities require visionOS 2.0+ devices on a FaceTime\ncall. The Simulator supports single-player layout testing but not multiplayer.\n\n### Project Configuration\n\n1. `import TabletopKit` in source files that define game logic.\n2. `import RealityKit` for entity-based rendering.\n3. For multiplayer, add the **Group Activities** capability in Signing &\n   Capabilities.\n4. Provide 3D assets (USDZ) in a RealityKit content bundle for tables, pieces,\n   cards, and dice.\n\n### Key Types Overview\n\n| Type | Role |\n|---|---|\n| `TabletopGame` | Central game manager; owns setup, actions, observers, rendering |\n| `TableSetup` | Configuration object passed to `TabletopGame` init |\n| `Tabletop` / `EntityTabletop` | Protocol for the table surface |\n| `Equipment` / `EntityEquipment` | Protocol for interactive game pieces |\n| `TableSeat` / `EntityTableSeat` | Protocol for player seat positions |\n| `TabletopAction` | Commands that modify game state |\n| `TabletopInteraction` | Gesture-driven player interactions with equipment |\n| `TabletopGame.Observer` | Callback protocol for reacting to confirmed actions |\n| `TabletopGame.RenderDelegate` | Callback protocol for visual updates |\n| `EntityRenderDelegate` | RealityKit-specific render delegate |\n\n## Game Configuration\n\nBuild a game in three steps: define the table, configure the setup, create the\n`TabletopGame` instance.\n\n```swift\nimport TabletopKit\nimport RealityKit\n\nlet table = GameTable()\nvar setup = TableSetup(tabletop: table)\nsetup.add(seat: PlayerSeat(index: 0, pose: seatPose0))\nsetup.add(seat: PlayerSeat(index: 1, pose: seatPose1))\nsetup.add(equipment: GamePawn(id: .init(1)))\nsetup.add(equipment: GameDie(id: .init(2)))\nsetup.register(action: MyCustomAction.self)\n\nlet game = TabletopGame(tableSetup: setup)\ngame.claimAnySeat()\n```\n\nCall `update(deltaTime:)` each frame if automatic updates are not enabled via\nthe `.tabletopGame(_:parent:automaticUpdate:)` modifier. Read state safely with\n`withCurrentSnapshot(_:)`.\n\n## Table and Board\n\n### Tabletop Protocol\n\nConform to `EntityTabletop` to define the playing surface. Provide a `shape`\n(round or rectangular) and a RealityKit `Entity` for visual representation.\n\n```swift\nstruct GameTable: EntityTabletop {\n    var shape: TabletopShape\n    var entity: Entity\n    var id: EquipmentIdentifier\n\n    init() {\n        entity = try! Entity.load(named: \"table/game_table\", in: contentBundle)\n        shape = .round(entity: entity)\n        id = .init(0)\n    }\n}\n```\n\n### Table Shapes\n\nUse factory methods on `TabletopShape`:\n\n```swift\n// Round table from dimensions\nlet round = TabletopShape.round(\n    center: .init(x: 0, y: 0, z: 0),\n    radius: 0.5,\n    thickness: 0.05,\n    in: .meters\n)\n\n// Rectangular table from entity\nlet rect = TabletopShape.rectangular(entity: tableEntity)\n```\n\n## Equipment (Pieces, Cards, Dice)\n\n### Equipment Protocol\n\nAll interactive game objects conform to `Equipment` (or `EntityEquipment` for\nRealityKit-rendered pieces). Each piece has an `id` (`EquipmentIdentifier`) and\nan `initialState` property.\n\nChoose the state type based on the equipment:\n\n| State Type | Use Case |\n|---|---|\n| `BaseEquipmentState` | Generic pieces, pawns, tokens |\n| `CardState` | Playing cards (tracks `faceUp` / face-down) |\n| `DieState` | Dice with an integer `value` |\n| `RawValueState` | Custom data encoded as `UInt64` |\n\n### Defining Equipment\n\n```swift\n// Pawn -- uses BaseEquipmentState\nstruct GamePawn: EntityEquipment {\n    var id: EquipmentIdentifier\n    var initialState: BaseEquipmentState\n    var entity: Entity\n\n    init(id: EquipmentIdentifier) {\n        self.id = id\n        self.entity = try! Entity.load(named: \"pieces/pawn\", in: contentBundle)\n        self.initialState = BaseEquipmentState(\n            parentID: .init(0), seatControl: .any,\n            pose: .identity, entity: entity\n        )\n    }\n}\n\n// Card -- uses CardState (tracks faceUp)\nstruct PlayingCard: EntityEquipment {\n    var id: EquipmentIdentifier\n    var initialState: CardState\n    var entity: Entity\n\n    init(id: EquipmentIdentifier) {\n        self.id = id\n        self.entity = try! Entity.load(named: \"cards/card\", in: contentBundle)\n        self.initialState = .faceDown(\n            parentID: .init(0), seatControl: .any,\n            pose: .identity, entity: entity\n        )\n    }\n}\n\n// Die -- uses DieState (tracks integer value)\nstruct GameDie: EntityEquipment {\n    var id: EquipmentIdentifier\n    var initialState: DieState\n    var entity: Entity\n\n    init(id: EquipmentIdentifier) {\n        self.id = id\n        self.entity = try! Entity.load(named: \"dice/d6\", in: contentBundle)\n        self.initialState = DieState(\n            value: 1, parentID: .init(0), seatControl: .any,\n            pose: .identity, entity: entity\n        )\n    }\n}\n```\n\n### ControllingSeats\n\nRestrict which players can interact with a piece via `seatControl`:\n- `.any` -- any player\n- `.restricted([seatID1, seatID2])` -- specific seats only\n- `.current` -- only the seat whose turn it is\n- `.inherited` -- inherits from parent equipment\n\n### Equipment Hierarchy and Layout\n\nEquipment can be parented to other equipment. Override `layoutChildren(for:visualState:)`\nto position children. Return one of:\n- `.planarStacked(layout:animationDuration:)` -- cards/tiles stacked vertically\n- `.planarOverlapping(layout:animationDuration:)` -- cards fanned or overlapping\n- `.volumetric(layout:animationDuration:)` -- full 3D layout\n\nSee [references/tabletopkit-patterns.md](references/tabletopkit-patterns.md) for card fan, grid, and overlap layout examples.\n\n## Player Seats\n\nConform to `EntityTableSeat` and provide a pose around the table:\n\n```swift\nstruct PlayerSeat: EntityTableSeat {\n    var id: TableSeatIdentifier\n    var initialState: TableSeatState\n    var entity: Entity\n\n    init(index: Int, pose: TableVisualState.Pose2D) {\n        self.id = TableSeatIdentifier(index)\n        self.entity = Entity()\n        self.initialState = TableSeatState(pose: pose, context: 0)\n    }\n}\n```\n\nClaim a seat before interacting: `game.claimAnySeat()`, `game.claimSeat(matching:)`,\nor `game.releaseSeat()`. Observe changes via `TabletopGame.Observer.playerChangedSeats`.\n\n## Game Actions and Turns\n\n### Built-in Actions\n\nUse `TabletopAction` factory methods to modify game state:\n\n```swift\n// Move equipment to a new parent\ngame.addAction(.moveEquipment(matching: pieceID, childOf: targetID, pose: newPose))\n\n// Flip a card face-up\ngame.addAction(.updateEquipment(card, faceUp: true))\n\n// Update die value\ngame.addAction(.updateEquipment(die, value: 6))\n\n// Set whose turn it is\ngame.addAction(.setTurn(matching: TableSeatIdentifier(1)))\n\n// Update a score counter\ngame.addAction(.updateCounter(matching: counterID, value: 100))\n\n// Create a state bookmark (for undo/reset)\ngame.addAction(.createBookmark(id: StateBookmarkIdentifier(1)))\n```\n\n### Custom Actions\n\nFor game-specific logic, conform to `CustomAction`:\n\n```swift\nstruct CollectCoin: CustomAction {\n    let coinID: EquipmentIdentifier\n    let playerID: EquipmentIdentifier\n\n    init?(from action: some TabletopAction) {\n        // Decode from generic action\n    }\n\n    func validate(snapshot: TableSnapshot) -> Bool {\n        // Return true if action is legal\n        true\n    }\n\n    func apply(table: inout TableState) {\n        // Mutate state directly\n    }\n}\n```\n\nRegister custom actions during setup:\n\n```swift\nsetup.register(action: CollectCoin.self)\n```\n\n### Score Counters\n\n```swift\nsetup.add(counter: ScoreCounter(id: .init(0), value: 0))\n// Update: game.addAction(.updateCounter(matching: .init(0), value: 42))\n// Read:   snapshot.counter(matching: .init(0))?.value\n```\n\n### State Bookmarks\n\nSave and restore game state for undo/reset:\n\n```swift\ngame.addAction(.createBookmark(id: StateBookmarkIdentifier(1)))\ngame.jumpToBookmark(matching: StateBookmarkIdentifier(1))\n```\n\n## Interactions\n\n### TabletopInteraction.Delegate\n\nReturn an interaction delegate from the `.tabletopGame` modifier to handle\nplayer gestures on equipment:\n\n```swift\n.tabletopGame(game.tabletopGame, parent: game.renderer.root) { value in\n    if game.tabletopGame.equipment(of: GameDie.self, matching: value.startingEquipmentID) != nil {\n        return DieInteraction(game: game)\n    }\n    return DefaultInteraction(game: game)\n}\n```\n\n### Handling Gestures and Tossing Dice\n\n```swift\nclass DieInteraction: TabletopInteraction.Delegate {\n    let game: Game\n\n    func update(interaction: TabletopInteraction) {\n        switch interaction.value.phase {\n        case .started:\n            interaction.setConfiguration(.init(allowedDestinations: .any))\n        case .update:\n            if interaction.value.gesture?.phase == .ended {\n                interaction.toss(\n                    equipmentID: interaction.value.controlledEquipmentID,\n                    as: .cube(height: 0.02, in: .meters)\n                )\n            }\n        case .ended, .cancelled:\n            break\n        }\n    }\n\n    func onTossStart(interaction: TabletopInteraction,\n                     outcomes: [TabletopInteraction.TossOutcome]) {\n        for outcome in outcomes {\n            let face = outcome.tossableRepresentation.face(for: outcome.restingOrientation)\n            interaction.addAction(.updateEquipment(\n                die, rawValue: face.rawValue, pose: outcome.pose\n            ))\n        }\n    }\n}\n```\n\n### Tossable Representations\n\nDice physics shapes: `.cube` (d6), `.tetrahedron` (d4), `.octahedron` (d8),\n`.decahedron` (d10), `.dodecahedron` (d12), `.icosahedron` (d20), `.sphere`.\nAll take `height:in:` (or `radius:in:` for sphere) and optional `restitution:`.\n\n### Programmatic Interactions\n\nStart interactions from code: `game.startInteraction(onEquipmentID: pieceID)`.\n\nSee [references/tabletopkit-patterns.md](references/tabletopkit-patterns.md) for group\ntoss, predetermined outcomes, interaction acceptance/rejection, and destination\nrestriction patterns.\n\n## RealityKit Rendering\n\nConform to `EntityRenderDelegate` to bridge state to RealityKit. Provide a\n`root` entity. TabletopKit automatically positions `EntityEquipment` entities.\n\n```swift\nclass GameRenderer: EntityRenderDelegate {\n    let root = Entity()\n\n    func onUpdate(timeInterval: Double, snapshot: TableSnapshot,\n                  visualState: TableVisualState) {\n        // Custom visual updates beyond automatic positioning\n    }\n}\n```\n\nConnect to SwiftUI with `.tabletopGame(_:parent:automaticUpdate:)` on a\n`RealityView`:\n\n```swift\nstruct GameView: View {\n    let game: Game\n\n    var body: some View {\n        RealityView { content in\n            content.entities.append(game.renderer.root)\n        }\n        .tabletopGame(game.tabletopGame, parent: game.renderer.root) { value in\n            GameInteraction(game: game)\n        }\n    }\n}\n```\n\nDebug outlines: `game.tabletopGame.debugDraw(options: [.drawTable, .drawSeats, .drawEquipment])`\n\n## Group Activities Integration\n\nTabletopKit integrates directly with GroupActivities for FaceTime-based\nmultiplayer. Define a `GroupActivity`, then call `coordinateWithSession(_:)`.\nTabletopKit automatically synchronizes all equipment state, seat assignments,\nactions, and interactions. No manual message passing required.\n\n```swift\nimport GroupActivities\n\nstruct BoardGameActivity: GroupActivity {\n    var metadata: GroupActivityMetadata {\n        var meta = GroupActivityMetadata()\n        meta.type = .generic\n        meta.title = \"Board Game\"\n        return meta\n    }\n}\n\n@Observable\nclass GroupActivityManager {\n    let tabletopGame: TabletopGame\n    private var sessionTask: Task<Void, Never>?\n\n    init(tabletopGame: TabletopGame) {\n        self.tabletopGame = tabletopGame\n        sessionTask = Task { @MainActor in\n            for await session in BoardGameActivity.sessions() {\n                tabletopGame.coordinateWithSession(session)\n            }\n        }\n    }\n\n    deinit { tabletopGame.detachNetworkCoordinator() }\n}\n```\n\nImplement `TabletopGame.MultiplayerDelegate` for `joinAccepted()`,\n`playerJoined(_:)`, `didRejectPlayer(_:reason:)`, and\n`multiplayerSessionFailed(reason:)`. See\n[references/tabletopkit-patterns.md](references/tabletopkit-patterns.md) for custom\nnetwork coordinators and arbiter role management.\n\n## Common Mistakes\n\n- **Forgetting platform restriction.** TabletopKit is visionOS-only. Do not\n  conditionally compile for iOS/macOS; the framework does not exist there.\n- **Skipping seat claim.** Players must call `claimAnySeat()` or `claimSeat(_:)`\n  before interacting with equipment. Without a seat, actions are rejected.\n- **Mutating state outside actions.** All state changes must go through\n  `TabletopAction` or `CustomAction`. Directly modifying equipment properties\n  bypasses synchronization.\n- **Missing custom action registration.** Custom actions must be registered with\n  `setup.register(action:)` before creating the `TabletopGame`. Unregistered\n  actions are silently dropped.\n- **Not handling action rollback.** Actions are optimistically applied and can be\n  rolled back if validation fails on the arbiter. Implement\n  `actionWasRolledBack(_:snapshot:)` to revert UI state.\n- **Using wrong parent ID.** Equipment `parentID` in state must reference a\n  valid equipment ID (typically the table or a container). An invalid parent\n  causes the piece to disappear.\n- **Ignoring TossOutcome faces.** After a toss, read the face from\n  `outcome.tossableRepresentation.face(for: outcome.restingOrientation)` rather\n  than generating a random value. The physics simulation determines the result.\n- **Testing multiplayer in Simulator.** Group Activities do not work in Simulator.\n  Multiplayer requires physical Apple Vision Pro devices on a FaceTime call.\n\n## Review Checklist\n\n- [ ] `import TabletopKit` present; target is visionOS 2.0+\n- [ ] `TableSetup` created with a `Tabletop`/`EntityTabletop` conforming type\n- [ ] All equipment conforms to `Equipment` or `EntityEquipment` with correct state type\n- [ ] Seats added and `claimAnySeat()` / `claimSeat(_:)` called at game start\n- [ ] All custom actions registered with `setup.register(action:)`\n- [ ] `TabletopGame.Observer` implemented for reacting to confirmed actions\n- [ ] `EntityRenderDelegate` or `RenderDelegate` connected\n- [ ] `.tabletopGame(_:parent:automaticUpdate:)` modifier on `RealityView`\n- [ ] `GroupActivity` defined and `coordinateWithSession(_:)` called for multiplayer\n- [ ] Group Activities capability added in Xcode for multiplayer builds\n- [ ] Debug visualization (`debugDraw`) disabled before release\n- [ ] Tested on device; multiplayer tested with 2+ Apple Vision Pro units\n\n## References\n\n- [references/tabletopkit-patterns.md](references/tabletopkit-patterns.md) -- extended patterns for observer implementation, custom actions, dice simulation, card overlap, and network coordination\n- [Apple Documentation: TabletopKit](https://sosumi.ai/documentation/tabletopkit)\n- [Creating tabletop games (sample code)](https://sosumi.ai/documentation/tabletopkit/creating-tabletop-games)\n- [Synchronizing group gameplay with TabletopKit (sample code)](https://sosumi.ai/documentation/tabletopkit/synchronizing-group-gameplay-with-tabletopkit)\n- [Simulating dice rolls as a component for your game (sample code)](https://sosumi.ai/documentation/tabletopkit/simulating-dice-rolls-as-a-component-for-your-game)\n- [Implementing playing card overlap and physical characteristics (sample code)](https://sosumi.ai/documentation/tabletopkit/implementing-playing-card-overlap-and-physical-characteristics)\n- [WWDC24 session 10091: Build a spatial board game](https://sosumi.ai/videos/play/wwdc2024/10091/)","tags":["tabletopkit","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-tabletopkit","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/tabletopkit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.684","qualityRationale":"deterministic score 0.68 from registry signals: · indexed on github topic:agent-skills · 468 github stars · SKILL.md body (16,483 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:53:45.805Z","embedding":null,"createdAt":"2026-04-18T22:01:29.539Z","updatedAt":"2026-04-22T00:53:45.805Z","lastSeenAt":"2026-04-22T00:53:45.805Z","tsv":"'/documentation/tabletopkit)':1631 '/documentation/tabletopkit/creating-tabletop-games)':1639 '/documentation/tabletopkit/implementing-playing-card-overlap-and-physical-characteristics)':1675 '/documentation/tabletopkit/simulating-dice-rolls-as-a-component-for-your-game)':1663 '/documentation/tabletopkit/synchronizing-group-gameplay-with-tabletopkit)':1649 '/videos/play/wwdc2024/10091/)':1686 '0':332,438,457,459,461,578,618,661,792,954,956,962,969 '0.02':1064 '0.05':465 '0.5':463 '1':176,339,347,658,866,887,985,989 '100':876 '10091':1678 '2':186,353,1604 '2.0':69,149,157,1523 '3':194 '3d':207,739 '4':205 '42':964 '6':856 '6.3':73 'acceptance/rejection':1141 'action':104,109,232,284,355,808,814,889,910,916,925,939,944,1255,1371,1377,1395,1398,1404,1410,1416,1418,1554,1558,1565,1618 'actionwasrolledback':1434 'activ':35,120,124,154,200,1229,1498,1584 'ad':1544,1586 'add':197 'alloweddestin':1050 'animationdur':724,730,737 'appl':1507,1605,1626 'appli':930,1421 'arbit':1330,1432 'around':761 'asset':208 'assign':1254 'automat':369,1161,1184,1248 'automaticupd':378,1192,1572 'await':1304 'back':1426 'base':192,511,1239 'baseequipmentst':519,549,558,575 'beyond':1183 'board':5,18,45,84,88,387,1278,1682 'boardgameact':1267 'boardgameactivity.sessions':1307 'bodi':1204 'bookmark':880,972 'bool':921 'break':1070 'bridg':1152 'build':13,299,1591,1679 'built':812 'built-in':811 'bundl':214 'bypass':1391 'call':162,363,1245,1360,1514,1548,1580 'callback':278,286 'cancel':1069 'capabl':201,204,1585 'card':20,91,96,218,479,526,585,731,745,840,846,1621,1666 'cards/card':611 'cards/tiles':725 'cardstat':524,587,598 'case':518,1046,1052,1067 'caus':1463 'center':454 'central':227 'chang':804,1380 'characterist':1670 'checklist':132,135,1516 'childof':834 'children':718 'choos':507 'claim':793,1357 'claimanyseat':1361,1546 'claimseat':1363,1547 'class':1034,1166,1283 'code':1128,1636,1646,1660,1672 'coinid':903 'collectcoin':900 'collectcoin.self':945 'command':264 'common':126,129,1333 'common-mistak':128 'compil':1346 'compon':1655 'condit':1345 'configur':78,81,175,236,298,308 'confirm':283,1564 'conform':390,487,754,895,1148,1530,1534 'connect':1186,1569 'contain':1459 'content':74,213,1208 'content.entities.append':1210 'contentbundl':431,573,613,654 'context':791 'controllingseat':668 'coordin':1328,1625 'coordinatewithsess':1246,1579 'correct':1540 'counter':870,947,950 'counterid':874 'creat':2,42,311,877,1406,1525,1632 'createbookmark':884,982 'cube':1062,1098 'current':688 'custom':539,888,938,1180,1326,1394,1397,1553,1617 'customact':897,901,1386 'd10':1105 'd12':1107 'd20':1109 'd4':1101 'd6':1099 'd8':1103 'data':540 'debug':1221,1592 'debugdraw':1594 'decahedron':1104 'decod':913 'defaultinteract':1025 'defin':183,305,394,544,1241,1577 'deinit':1310 'deleg':296,995 'deltatim':365 'destin':1143 'determin':1490 'devic':158,1510,1600 'dice':22,92,97,220,480,533,1032,1095,1619,1651 'dice/d6':652 'didrejectplay':1317 'die':625,850,854,1088 'dieinteract':1021,1035 'diestat':532,627,639,656 'dimens':450 'direct':936,1233,1387 'disabl':1595 'disappear':1467 'document':1627 'dodecahedron':1106 'doubl':1175 'drawequip':1227 'drawseat':1226 'drawtabl':1225 'driven':272 'drop':1413 'element':38 'enabl':373 'encod':541 'end':1057,1068 'entiti':191,407,419,420,425,434,435,471,475,560,561,583,584,600,601,623,624,641,642,666,667,775,776,786,1159,1164,1171 'entity-bas':190 'entity.load':427,569,609,650 'entityequip':250,491,552,592,633,1163,1538 'entityrenderdeleg':291,1150,1168,1566 'entitytableseat':257,756,767 'entitytabletop':243,392,414,1529 'equip':57,89,94,249,276,343,349,477,481,489,514,545,700,701,705,711,825,1005,1251,1367,1389,1444,1452,1533,1536 'equipment-pieces-cards-dic':93 'equipmentid':1059 'equipmentidentifi':423,502,555,564,595,604,636,645,904,907 'exampl':751 'exclus':143 'exist':1353 'experi':16 'extend':1612 'face':530,842,1082,1470,1476 'face-down':529 'face-up':841 'face.rawvalue':1090 'facedown':615 'facetim':32,161,1238,1513 'facetime-bas':1237 'faceup':528,589,847 'factori':442,817 'fail':1429 'fan':732,746 'featur':151 'file':181 'flip':838 'forget':1335 'frame':367 'framework':1350 'full':738 'func':917,929,1040,1071,1172 'game':6,15,29,37,46,55,77,80,103,108,184,228,254,267,297,301,358,485,807,821,892,976,1022,1023,1026,1027,1038,1039,1201,1202,1219,1220,1279,1550,1634,1658,1683 'game-actions-and-turn':107 'game-configur':79 'game-specif':891 'game.addaction':830,844,852,862,871,883,958,981 'game.claimanyseat':362,798 'game.claimseat':799 'game.jumptobookmark':986 'game.releaseseat':802 'game.renderer.root':1010,1211,1215 'game.startinteraction':1129 'game.tabletopgame':1008,1213 'game.tabletopgame.debugdraw':1223 'game.tabletopgame.equipment':1014 'gamedi':350,632 'gamedie.self':1016 'gameinteract':1218 'gamepawn':344,551 'gameplay':1642 'gamerender':1167 'gamet':322,413 'gameview':1198 'generat':1483 'generic':520,915,1276 'gestur':271,1003,1029 'gesture-driven':270 'go':1382 'grid':747 'group':34,119,123,153,199,1136,1228,1497,1583,1641 'group-activities-integr':122 'groupact':1235,1243,1265,1268,1576 'groupactivitymanag':1284 'groupactivitymetadata':1271,1274 'handl':54,1001,1028,1415 'height':1063,1113 'hierarchi':702 'icosahedron':1108 'id':345,351,422,436,501,554,563,566,594,603,606,635,644,647,769,885,952,983,1443,1453 'ident':582,622,665 'ignor':1468 'implement':1312,1433,1560,1616,1664 'import':177,187,316,318,1264,1517 'index':331,338,778,784 'inherit':696,697 'init':241,346,352,424,437,455,562,577,602,617,643,660,777,908,953,961,968,1049,1294 'initialst':505,557,597,638,772 'inout':932 'instanc':314 'int':779 'integ':536,629 'integr':121,125,1230,1232 'interact':58,112,113,253,274,484,673,797,990,994,1042,1073,1124,1126,1140,1257,1365 'interaction.addaction':1086 'interaction.setconfiguration':1048 'interaction.toss':1058 'interaction.value.controlledequipmentid':1060 'interaction.value.gesture':1055 'interaction.value.phase':1045 'invalid':1461 'ios/macos':1348 'joinaccept':1315 'key':221 'layout':56,169,704,723,729,736,740,750 'layoutchildren':713 'legal':927 'let':320,357,451,472,902,905,1037,1081,1169,1200,1285 'logic':185,894 'mainactor':1301 'manag':23,62,229,1332 'manual':1259 'match':800,832,864,873,960,967,987,1017 'messag':1260 'meta':1273,1281 'meta.title':1277 'meta.type':1275 'metadata':1270 'meter':467,1066 'method':443,818 'miss':1393 'mistak':127,130,1334 'modifi':266,379,820,999,1388,1573 'move':824 'moveequip':831 'multiplay':3,43,150,173,196,1240,1494,1504,1582,1590,1601 'multiplayersessionfail':1320 'must':1359,1381,1399,1448 'mutat':934,1374 'mycustomaction.self':356 'name':428,570,610,651 'network':1327,1624 'never':1293 'new':828 'newpos':837 'nil':1019 'object':237,486 'observ':233,803,1282,1615 'octahedron':1102 'one':720 'onequipmentid':1130 'ontossstart':1072 'onupd':1173 'optimist':1420 'option':1121,1224 'outcom':1075,1078,1080,1139 'outcome.pose':1092 'outcome.restingorientation':1085,1480 'outcome.tossablerepresentation.face':1083,1478 'outlin':1222 'outsid':1376 'overlap':734,749,1622,1667 'overrid':712 'overview':223 'own':230 'parent':377,699,708,829,1009,1191,1214,1442,1462,1571 'parentid':576,616,659,1445 'pass':238,1261 'pattern':1145,1613 'pawn':522,547 'phase':1056 'physic':1096,1488,1506,1669 'piec':19,90,95,217,255,478,496,498,521,676,1465 'pieceid':833,1131 'pieces/pawn':571 'planaroverlap':728 'planarstack':722 'platform':139,1336 'play':396,525,1665 'player':24,59,98,101,168,260,273,671,681,752,1002,1358 'player-seat':100 'playerid':906 'playerjoin':1316 'playerseat':330,337,766 'playingcard':591 'pose':333,340,581,621,664,760,780,789,790,836,1091 'posit':262,717,1162,1185 'predetermin':1138 'present':1519 'privat':1288 'pro':1509,1607 'programmat':1123 'project':174 'properti':506,1390 'protocol':244,251,258,279,287,389,482 'provid':206,398,758,1156 'radius':462,1116 'random':1485 'rather':1481 'rawvalu':1089 'rawvaluest':538 'react':281,1562 'read':380,965,1474 'real':40 'realitykit':66,114,117,188,212,293,319,406,494,1146,1155 'realitykit-rend':116,493 'realitykit-specif':292 'realityview':1195,1207,1575 'reason':1318,1321 'rect':473 'rectangular':403,468 'refer':136,137,1449,1609 'references/tabletopkit-patterns.md':742,743,1133,1134,1323,1324,1610,1611 'regist':937,1401,1555 'registr':1396 'reject':1373 'releas':1597 'render':36,67,115,118,193,234,295,495,1147 'renderdeleg':1568 'represent':410,1094 'requir':140,147,155,1262,1505 'restitut':1122 'restor':975 'restrict':669,682,1144,1337 'result':1492 'return':719,922,992,1020,1024,1280 'revert':1437 'review':131,134,1515 'review-checklist':133 'role':225,1331 'roll':1425,1652 'rollback':1417 'root':1158,1170 'round':401,433,447,452 'safe':382 'sampl':1635,1645,1659,1671 'save':973 'score':869,946 'scorecount':951 'seat':25,60,99,102,261,329,336,686,691,753,795,1253,1356,1370,1543 'seatcontrol':579,619,662,678 'seatid1':683 'seatid2':684 'seatpose0':334 'seatpose1':341 'see':741,1132,1322 'self.entity':567,607,648,785 'self.id':565,605,646,782 'self.initialstate':574,614,655,787 'self.tabletopgame':1297 'session':1305,1309,1677 'sessiontask':1290,1299 'set':857 'setturn':863 'setup':75,76,138,231,310,324,361,941 'setup.add':328,335,342,348,949 'setup.register':354,943,1403,1557 'shape':400,416,432,440,1097 'sign':203 'silent':1412 'simul':164,1489,1496,1503,1620,1650 'singl':167 'single-play':166 'skill' 'skill-tabletopkit' 'skip':1355 'snapshot':919,1176,1435 'snapshot.counter':966 'sosumi.ai':1630,1638,1648,1662,1674,1685 'sosumi.ai/documentation/tabletopkit)':1629 'sosumi.ai/documentation/tabletopkit/creating-tabletop-games)':1637 'sosumi.ai/documentation/tabletopkit/implementing-playing-card-overlap-and-physical-characteristics)':1673 'sosumi.ai/documentation/tabletopkit/simulating-dice-rolls-as-a-component-for-your-game)':1661 'sosumi.ai/documentation/tabletopkit/synchronizing-group-gameplay-with-tabletopkit)':1647 'sosumi.ai/videos/play/wwdc2024/10091/)':1684 'sourc':180 'source-dpearson2699' 'spatial':4,44,1681 'specif':294,685,893 'sphere':1110,1119 'stack':726 'start':1047,1125,1551 'state':30,63,268,381,509,515,822,879,935,971,977,1153,1252,1375,1379,1439,1447,1541 'statebookmarkidentifi':886,984,988 'step':304 'struct':412,550,590,631,765,899,1197,1266 'support':165 'surfac':51,248,397 'swift':72,315,411,446,546,764,823,898,942,948,980,1006,1033,1165,1196,1263 'swiftui':1188 'switch':1044 'synchron':28,64,1249,1392,1640 'tabl':50,82,86,216,247,307,321,327,385,439,448,469,763,931,1456 'table-and-board':85 'table/game_table':429 'tableent':476 'tableseat':256 'tableseatidentifi':770,783,865 'tableseatst':773,788 'tablesetup':235,325,360,1524 'tablesnapshot':920,1177 'tablest':933 'tabletop':14,242,326,388,1528,1633 'tabletopact':263,816,912,1384 'tabletopgam':226,240,313,359,376,998,1007,1190,1212,1286,1287,1295,1296,1298,1408,1570 'tabletopgame.coordinatewithsession':1308 'tabletopgame.detachnetworkcoordinator':1311 'tabletopgame.multiplayerdelegate':1313 'tabletopgame.observer':277,1559 'tabletopgame.observer.playerchangedseats':806 'tabletopgame.renderdelegate':285 'tabletopinteract':269,1043,1074 'tabletopinteraction.delegate':991,1036 'tabletopinteraction.tossoutcome':1076 'tabletopkit':1,8,41,53,141,178,317,1160,1231,1247,1338,1518,1628,1644 'tabletopshap':417,445 'tabletopshape.rectangular':474 'tabletopshape.round':453 'tablevisualst':1179 'tablevisualstate.pose2d':781 'take':1112 'target':71,1520 'targetid':835 'task':1291,1300 'test':170,1493,1598,1602 'tetrahedron':1100 'thick':464 'three':303 'timeinterv':1174 'token':523 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'toss':1031,1137,1473 'tossabl':1093 'tossoutcom':1469 'track':527,588,628 'tri':426,568,608,649 'true':848,923,928 'turn':27,61,106,111,693,810,859 'type':222,224,510,516,1531,1542 'typic':1454 'ui':1438 'uint64':543 'undo/reset':882,979 'unit':1608 'unregist':1409 'updat':290,364,370,849,867,957,1041,1053,1182 'updatecount':872,959 'updateequip':845,853,1087 'usdz':209 'use':7,11,52,152,441,517,548,586,626,815,1440 'valid':918,1428,1451 'valu':537,630,657,851,855,875,955,963,970,1011,1216,1486 'value.startingequipmentid':1018 'var':323,415,418,421,553,556,559,593,596,599,634,637,640,768,771,774,1203,1269,1272,1289 'vertic':727 'via':374,677,805 'view':1199,1206 'virtual':49 'vision':1508,1606 'visiono':10,68,145,148,156,1341,1522 'visionos-on':1340 'visual':289,409,1181,1593 'visualst':715,1178 'void':1292 'volumetr':735 'whose':692,858 'withcurrentsnapshot':384 'without':1368 'work':1501 'wrong':1441 'wwdc24':1676 'x':456 'xcode':1588 'y':458 'z':460","prices":[{"id":"3c9fa6d3-9075-46b4-a81b-3ee545e13c7c","listingId":"30a9be2a-c406-4b6e-933f-59193761d950","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:01:29.539Z"}],"sources":[{"listingId":"30a9be2a-c406-4b6e-933f-59193761d950","source":"github","sourceId":"dpearson2699/swift-ios-skills/tabletopkit","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/tabletopkit","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:29.539Z","lastSeenAt":"2026-04-22T00:53:45.805Z"}],"details":{"listingId":"30a9be2a-c406-4b6e-933f-59193761d950","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"tabletopkit","github":{"repo":"dpearson2699/swift-ios-skills","stars":468,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-21T19:26:16Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"0824df57a48bb5fe31e66d17ceaf6c97581b9131","skill_md_path":"skills/tabletopkit/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/tabletopkit"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"tabletopkit","description":"Create multiplayer spatial board games using TabletopKit on visionOS. Use when building tabletop game experiences with boards, pieces, cards, and dice, managing player seats and turns, synchronizing game state over FaceTime with Group Activities, rendering game elements with RealityKit, or implementing piece snapping and physics on a virtual table surface."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/tabletopkit"},"updatedAt":"2026-04-22T00:53:45.805Z"}}