# 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. ```swift 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** ```swift let card = Card(suit: .spades, rank: .king) print(card.displayValue) // "K" print(card.suit.symbol) // "♠" ``` ### 🎰 Chips **ChipView** - A casino chip with denomination display. ```swift ChipView(denomination: .hundred, size: 60, isSelected: true) ``` **ChipSelectorView** - Horizontal chip selector. ```swift @State var selectedChip: ChipDenomination = .hundred ChipSelectorView( denominations: ChipDenomination.allCases, selectedDenomination: $selectedChip ) ``` **ChipStackView** - Stacked chips showing bet amount. ```swift ChipStackView(amount: 500, chipColor: .red) ``` **Chip Denominations** (with standard casino colors) - `.one` ($1) - White/Light Blue - `.five` ($5) - Red - `.twentyFive` ($25) - Green - `.hundred` ($100) - Black - `.fiveHundred` ($500) - Purple - `.thousand` ($1,000) - Yellow - `.fiveThousand` ($5,000) - Brown - `.tenThousand` ($10,000) - Gray (custom) - `.twentyFiveThousand` ($25,000) - Teal (plaque style) - `.hundredThousand` ($100,000) - Burgundy (VIP) ### 📋 Sheets & Popups **SheetContainerView** - Consistent modal sheet styling. ```swift 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. ```swift SheetSection(title: "SECTION TITLE", icon: "star.fill") { // Your content } ``` ### 🎨 Branding & Icons **AppIconView** - Generate app icons with consistent styling. ```swift // 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. ```swift LaunchScreenView(config: .baccarat) ``` **IconRenderer** - Render views to images. ```swift // 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. ```swift // 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:** ```swift 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. ```swift // 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() // 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:** ```swift 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. ```swift // 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. ```swift 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 ```swift import SwiftUI import CasinoKit ``` ### 2. Create Your Game View ```swift 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 ```swift 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 ```swift // 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: ```swift // ✅ Good .padding(CasinoDesign.Spacing.medium) .opacity(CasinoDesign.Opacity.heavy) // ❌ Bad .padding(12) .opacity(0.8) ``` ### Localization Always pass localized strings for button text: ```swift 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: ```swift @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.