Dice
An RPG dice rolling app for iOS built with SwiftUI. Originally developed as “DiceRoller,” the project has evolved to include a “Music Mode” with Rest dice features, accessible via a multi-mode navigation interface.
Key Decisions
| Decision | Rationale | Outcome |
|---|---|---|
| SwiftUI only, no UIKit | Modern framework, cleaner code for new iOS projects | Good — clean declarative UI throughout |
| iOS 17.0 minimum target | Access to modern SwiftUI features like @Observable | Good — streamlined development |
| @MainActor on DiceCollection | Thread-safe UI updates for ObservableObject | Good — no concurrency issues |
| Factory method pattern for DiceRoll | Separates data model from roll action via static roll() | Good — clean separation of concerns |
| View component extraction | DiceSelectionView, SelectedDiceView, RollResultsView as separate views | Good — maintainable, reusable |
| Spring animation (0.3 response, 0.6-0.7 damp) | Snappy but smooth feel across all animations | Good — satisfying tactile feedback |
| Medium impact haptic for roll action | Primary action gets satisfying physical feedback | Good — enhances UX |
| AloysiusTheme enum for colors | Single source of truth for design tokens via hex Color extension | Good — easy visual consistency |
| Mode-agnostic UniversalRoll | Supports both standard and music dice results in a shared UI | Good — shared UI for roll results |
| DiceFace protocol for music dice | Common interface for all music dice face types (notes, chords, etc.) | Good — extensible music system |
Patterns Established
| Pattern | Description | Reusable |
|---|---|---|
| PressableButtonStyle | Consistent press feedback on all interactive buttons | Yes |
| Color.init(hex:) extension | Hex string to SwiftUI Color conversion | Yes |
| AloysiusTheme enum with static colors | Centralized design token system | Yes |
| DiceType CaseIterable enum | Enum with computed properties for sides and display name | Yes |
| DiceCollection as ObservableObject | Centralized state for dice selection and rolling | Yes |
| Static factory method on DiceRoll | DiceRoll.roll(_:) creates a roll with random result | Yes |
| MainTabView with AppMode enum | Tab-based mode switching with Identifiable enum | Yes |
Lessons Learned
| Lesson | Context | Impact |
|---|---|---|
| DispatchQueue.main.asyncAfter timing | State updates while UI shows animation | Smooth user experience between action and result |
| Dashed border for empty state UI | Visual invitation for interactivity | Improved UX for new users |
| Conditional bulk-action buttons | Only show clear-all when 2+ items are present | Reduced UI clutter |
| Gold/accent color for prominent values | Highlighting primary data points like the total sum | Better visual hierarchy |
| Legacy color aliases for migration | Using static aliases to ease theme refactoring | Seamless visual transitions during design updates |
| Repo renames require internal audit | Rename only affects GitHub; project files and bundle IDs must be updated | Prevents mismatch between repo and app code |
Technical Debt
| Item | Priority | Notes |
|---|---|---|
| Gesture interactions pending | Medium | Phase 9 (swipe, long-press, drag) planned but not started |
| Layout refresh pending | Medium | Phase 10 (improved responsiveness) planned but not started |
| No unit tests | Low | Codebase growing, may benefit from automated verification |
| Legacy color aliases in theme | Low | Deprecated static aliases still present for compatibility |
Related
- DiceRoller - Previous project name / consolidated entry
- PressableButtonStyle - Reusable button pattern
- DiceCollection as ObservableObject - Reusable view model pattern
- SwiftUI Dice Animation - Animation research