CasinoGames/CasinoKit/README.md

12 KiB

CasinoKit

A reusable Swift Package for building casino card games with SwiftUI. This package provides common components, themes, and utilities shared across casino game apps.

Requirements

  • iOS 17.0+
  • Swift 6.0+
  • Xcode 16.0+

Installation

This package is included as a local package in the workspace. To use in another project:

  1. Copy the CasinoKit folder to your project
  2. In Xcode: File → Add Package Dependencies → Add Local
  3. Select the CasinoKit folder

Features

🎴 Cards

CardView - A playing card with flip animation support.

import CasinoKit

// Face-up card
CardView(card: Card(suit: .hearts, rank: .ace), faceUp: true)

// Face-down card
CardView(card: card, faceUp: false)

// Empty placeholder (dotted outline)
CardPlaceholderView()

Card Model

let card = Card(suit: .spades, rank: .king)
print(card.displayValue)  // "K"
print(card.suit.symbol)   // "♠"

🎰 Chips

ChipView - A casino chip with denomination display.

ChipView(denomination: .hundred, size: 60, isSelected: true)

ChipSelectorView - Horizontal chip selector.

@State var selectedChip: ChipDenomination = .hundred

ChipSelectorView(
    denominations: ChipDenomination.allCases,
    selectedDenomination: $selectedChip
)

ChipStackView - Stacked chips showing bet amount.

ChipStackView(amount: 500, chipColor: .red)

Chip Denominations

  • .one (1)
  • .five (5)
  • .twentyFive (25)
  • .hundred (100)
  • .fiveHundred (500)
  • .thousand (1000)

📋 Sheets & Popups

SheetContainerView - Consistent modal sheet styling.

SheetContainerView(
    title: "Settings",
    content: {
        SheetSection(title: "DISPLAY", icon: "eye") {
            Toggle("Dark Mode", isOn: $darkMode)
        }
        
        SheetSection(title: "SOUND", icon: "speaker.wave.2") {
            Slider(value: $volume)
        }
    },
    onCancel: { dismiss() },
    onDone: { save(); dismiss() },
    doneButtonText: String(localized: "Done"),
    cancelButtonText: String(localized: "Cancel")
)

SheetSection - Styled section within sheets.

SheetSection(title: "SECTION TITLE", icon: "star.fill") {
    // Your content
}

🎨 Branding & Icons

AppIconView - Generate app icons with consistent styling.

// Use a preset
AppIconView(config: .baccarat, size: 1024)

// Custom configuration
let config = AppIconConfig(
    title: "BLACKJACK",
    subtitle: "21",
    iconSymbol: "suit.club.fill",
    primaryColor: Color(red: 0.1, green: 0.2, blue: 0.35),
    secondaryColor: Color(red: 0.05, green: 0.12, blue: 0.25),
    accentColor: .yellow
)
AppIconView(config: config, size: 1024)

Preset Configurations:

  • .baccarat - Spade symbol
  • .blackjack - Club symbol with "21" subtitle
  • .poker - Diamond symbol, red accent
  • .roulette - Grid symbol, red theme

LaunchScreenView - Animated splash screen.

LaunchScreenView(config: .baccarat)

IconRenderer - Render views to images.

// Render single icon
let image = IconRenderer.renderAppIcon(config: .baccarat, size: 1024)

// Render all iOS sizes
let allImages = IconRenderer.renderAllSizes(config: .baccarat)

🔊 Audio & Haptics

SoundManager - Manages game sounds and haptic feedback.

// Access shared instance
let sound = SoundManager.shared

// Configure settings
sound.soundEnabled = true
sound.hapticsEnabled = true
sound.volume = 1.0

// Play sounds
sound.play(.chipPlace)
sound.play(.cardDeal)
sound.play(.win)

// Convenience methods (include haptics)
sound.playChipPlace()   // Chip sound + light haptic
sound.playCardFlip()    // Card sound + light haptic
sound.playWin()         // Win sound + success haptic
sound.playLose()        // Lose sound + error haptic
sound.playGameOver()    // Game over sound + error haptic

Available Sounds:

Sound Description
.chipPlace Placing a bet
.chipStack Stacking chips
.cardDeal Dealing a card
.cardFlip Flipping a card
.cardShuffle Shuffling deck
.win Player wins
.lose Player loses
.push Tie/push
.bigWin Large payout
.buttonTap UI tap
.newRound Starting new round
.clearBets Clearing bets
.gameOver Out of chips

Custom Sound Files:

The manager uses iOS system sounds as fallback. To use custom sounds:

  1. Add .mp3 files named: chip_place.mp3, card_deal.mp3, win.mp3, etc.
  2. Add them to your app bundle's Resources folder
  3. Files are automatically detected and used

Haptic Methods:

sound.hapticLight()    // Light tap
sound.hapticMedium()   // Medium tap
sound.hapticHeavy()    // Heavy tap
sound.hapticSuccess()  // Success notification
sound.hapticError()    // Error notification
sound.hapticWarning()  // Warning notification

💾 Cloud Storage

CloudSyncManager - Saves game data locally and syncs with iCloud.

// 1. Define your game's data structure
struct MyGameData: PersistableGameData {
    static let gameIdentifier = "mygame"
    var roundsPlayed: Int { rounds.count }
    var lastModified: Date
    
    static var empty: MyGameData {
        MyGameData(rounds: [], balance: 10000, lastModified: Date())
    }
    
    var rounds: [RoundData]
    var balance: Int
}

// 2. Create sync manager
let persistence = CloudSyncManager<MyGameData>()

// 3. Save data (auto-syncs to iCloud)
var data = persistence.data
data.balance = 5000
persistence.save(data)

// 4. Data is automatically loaded on init
print(persistence.data.balance)

// 5. Check sync status
if persistence.iCloudAvailable {
    print("Last sync: \(persistence.lastSyncDate)")
}

// 6. Force sync
persistence.sync()

// 7. Listen for changes from other devices
persistence.onCloudDataReceived = { newData in
    print("Got \(newData.roundsPlayed) rounds from iCloud")
}

// 8. Reset all data
persistence.reset()

PersistableGameData Protocol:

public protocol PersistableGameData: Codable, Sendable {
    static var gameIdentifier: String { get }  // e.g., "baccarat"
    var roundsPlayed: Int { get }               // For conflict resolution
    var lastModified: Date { get set }          // Updated automatically
    static var empty: Self { get }              // Default/new game state
}

Features:

  • 📱 Local Storage - Always saved to UserDefaults
  • ☁️ iCloud Sync - Automatic sync when signed in
  • 🔄 Conflict Resolution - Uses roundsPlayed to pick newer data
  • 📢 Change Notifications - Callbacks when data changes from other devices
  • 🔒 Privacy - Uses Apple ID, no Game Center required

🎨 Design System

CasinoDesign - Shared design constants.

// Spacing
CasinoDesign.Spacing.small      // 8
CasinoDesign.Spacing.medium     // 12
CasinoDesign.Spacing.large      // 16

// Corner Radius
CasinoDesign.CornerRadius.small   // 8
CasinoDesign.CornerRadius.medium  // 12
CasinoDesign.CornerRadius.large   // 16

// Font Sizes (base values for @ScaledMetric)
CasinoDesign.BaseFontSize.small   // 12
CasinoDesign.BaseFontSize.body    // 14
CasinoDesign.BaseFontSize.large   // 20

// Opacity
CasinoDesign.Opacity.subtle   // 0.05
CasinoDesign.Opacity.light    // 0.2
CasinoDesign.Opacity.medium   // 0.5
CasinoDesign.Opacity.heavy    // 0.8

// Animation
CasinoDesign.Animation.quick          // 0.2
CasinoDesign.Animation.standard       // 0.3
CasinoDesign.Animation.springDuration // 0.4

Color.Sheet - Sheet/popup colors.

Color.Sheet.background      // Dark background
Color.Sheet.sectionFill     // Section card fill
Color.Sheet.accent          // Yellow accent
Color.Sheet.secondaryText   // Muted text
Color.Sheet.cancelText      // Cancel button text

🌍 Localization

CasinoKit includes localization for:

  • English (en)
  • Spanish - Mexico (es-MX)
  • French - Canada (fr-CA)

Localized Strings:

  • Card names (Ace, King, Queen, etc.)
  • Suit names (Hearts, Diamonds, Clubs, Spades)
  • Chip-related strings
  • Accessibility labels

Adding Localizations:

The package uses String Catalogs (.xcstrings). Edit:

CasinoKit/Sources/CasinoKit/Resources/Localizable.xcstrings

Usage in a New Game

1. Import the Package

import SwiftUI
import CasinoKit

2. Create Your Game View

struct BlackjackTableView: View {
    @State private var deck = Deck(numberOfDecks: 6)
    @State private var selectedChip: ChipDenomination = .hundred
    
    var body: some View {
        VStack {
            // Player's hand
            HStack {
                ForEach(playerCards) { card in
                    CardView(card: card, faceUp: true)
                }
            }
            
            // Chip selector
            ChipSelectorView(
                denominations: ChipDenomination.allCases,
                selectedDenomination: $selectedChip
            )
        }
    }
}

3. Create Settings Sheet

struct SettingsView: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        SheetContainerView(title: "Settings") {
            SheetSection(title: "GAME OPTIONS", icon: "gearshape") {
                // Your settings
            }
        } onDone: {
            dismiss()
        }
    }
}

4. Generate App Icon

// In a preview or development view
#Preview {
    let config = AppIconConfig(
        title: "BLACKJACK",
        subtitle: "21",
        iconSymbol: "suit.club.fill"
    )
    return AppIconView(config: config, size: 512)
}

Screenshot the preview and add to your Assets.xcassets.

File Structure

CasinoKit/
├── Package.swift
├── README.md
├── Sources/CasinoKit/
│   ├── CasinoKit.swift
│   ├── Exports.swift
│   ├── Models/
│   │   ├── Card.swift
│   │   ├── Deck.swift
│   │   └── ChipDenomination.swift
│   ├── Views/
│   │   ├── Cards/
│   │   │   └── CardView.swift
│   │   ├── Chips/
│   │   │   ├── ChipView.swift
│   │   │   ├── ChipSelectorView.swift
│   │   │   ├── ChipStackView.swift
│   │   │   └── ChipOnTableView.swift
│   │   ├── Sheets/
│   │   │   └── SheetContainerView.swift
│   │   └── Branding/
│   │       ├── AppIconView.swift
│   │       ├── LaunchScreenView.swift
│   │       └── IconRenderer.swift
│   ├── Audio/
│   │   └── SoundManager.swift
│   ├── Theme/
│   │   ├── CasinoTheme.swift
│   │   └── CasinoDesign.swift
│   └── Resources/
│       ├── Localizable.xcstrings
│       └── Sounds/
│           └── README.md (+ .mp3 files)
└── Tests/CasinoKitTests/
    └── CasinoKitTests.swift

Best Practices

Design Constants

Always use CasinoDesign constants instead of magic numbers:

// ✅ Good
.padding(CasinoDesign.Spacing.medium)
.opacity(CasinoDesign.Opacity.heavy)

// ❌ Bad
.padding(12)
.opacity(0.8)

Localization

Always pass localized strings for button text:

SheetContainerView(
    title: String(localized: "Settings"),
    content: { ... },
    onDone: { dismiss() },
    doneButtonText: String(localized: "Done")
)

Accessibility

All components include VoiceOver support:

  • Cards announce suit and rank
  • Chips announce denomination
  • Interactive elements have labels and hints

Dynamic Type

Use @ScaledMetric with base font sizes:

@ScaledMetric(relativeTo: .body) 
private var fontSize: CGFloat = CasinoDesign.BaseFontSize.body

Apps Using CasinoKit

  • Baccarat - The classic casino card game

Version History

  • 1.0.0 - Initial release
    • Card and Chip components
    • Sheet container views
    • App icon and launch screen generators
    • Localization support (EN, ES-MX, FR-CA)

License

This package is for personal use in your casino game projects.