Agent skill
milestone-celebration
Generates achievement celebration UI with confetti animations, badge unlocks, progress milestones, haptic feedback, and optional share-to-social. Use when user wants to celebrate achievements, show confetti, display milestone badges, or trigger rewards on key thresholds.
Install this agent skill to your Project
npx add-skill https://github.com/rshankras/claude-code-apple-skills/tree/main/skills/generators/milestone-celebration
SKILL.md
Milestone Celebration Generator
Generate a production milestone celebration system with confetti particle animations via CAEmitterLayer, achievement badge views with locked/unlocked states, a celebration overlay with spring animations, haptic feedback, and optional shareable achievement cards — all triggered automatically when users hit key thresholds.
When This Skill Activates
Use this skill when the user:
- Asks to "add celebrations" or "celebrate achievements"
- Wants "confetti animation" or "particle effects for milestones"
- Mentions "achievement badges" or "badge unlock animation"
- Asks about "milestone rewards" or "progress milestones"
- Wants to "celebrate achievement" or "trigger celebration on threshold"
- Asks about "level-up animation" or "gamification celebrations"
Pre-Generation Checks
1. Project Context Detection
- Check Swift version (requires Swift 5.9+)
- Check deployment target (iOS 17+ / macOS 14+ for
@Observable, spring animations,sensoryFeedback) - Check for
UIKitavailability (iOS —CAEmitterLayerfor confetti, haptics) - Identify source file locations
2. Conflict Detection
Search for existing celebration/animation code:
Glob: **/*Confetti*.swift, **/*Celebration*.swift, **/*Achievement*.swift, **/*Milestone*.swift, **/*Badge*.swift
Grep: "CAEmitterLayer" or "CAEmitterCell" or "UINotificationFeedbackGenerator" or "confetti" or "celebration"
If existing celebration or gamification library found (e.g., custom confetti, third-party particle libraries):
- Ask if user wants to replace or integrate with it
- If keeping, advise on best practices instead of generating
3. Platform Detection
Determine if generating for iOS (UIKit-based confetti + haptics) or macOS (Core Animation confetti, no haptics) or both (cross-platform with feature gates).
Configuration Questions
Ask user via AskUserQuestion:
-
Celebration type? (multi-select)
- Confetti burst (full-screen particle animation)
- Badge unlock (icon reveal with glow animation)
- Level-up (progress ring fill + title transition)
- Custom (user defines their own celebration style)
-
Haptic feedback?
- Yes — success haptic on celebration trigger (recommended)
- No — silent celebrations only
-
Shareable achievement card?
- Yes — render achievement as image for social sharing via
ShareLink - No — in-app celebration only
- Yes — render achievement as image for social sharing via
-
Sound effects?
- Yes — play system sound or bundled audio on celebration
- No — visual only
Generation Process
Step 1: Read Templates
Read templates.md for production Swift code.
Step 2: Create Core Files
Generate these files:
Milestone.swift— Model with id, title, description, threshold, icon, unlock state.Codable+Sendable.MilestoneTracker.swift—@Observableclass tracking progress toward milestones, checking thresholds, triggering celebrations. Persists unlock state viaUserDefaultsor file storage.ConfettiView.swift—UIViewRepresentablewrappingCAEmitterLayerfor confetti particle animation. Configurable colors, duration, density. Respects Reduce Motion.
Step 3: Create UI Files
CelebrationOverlay.swift— Full-screen overlay combining confetti + badge reveal + congratulations message. Auto-dismisses after configurable duration. UseswithAnimation(.spring).MilestoneBadgeView.swift— Individual badge view with locked/unlocked states, SF Symbol icon, progress ring for partial progress.MilestoneCollectionView.swift— Grid layout of all milestones showing locked/unlocked state with progress indicators.
Step 4: Create Optional Files
Based on configuration:
HapticManager.swift— If haptic feedback selected (thin wrapper aroundUINotificationFeedbackGenerator/UIImpactFeedbackGenerator)ShareableMilestoneCard.swift— If shareable selected (renders milestone as image viaImageRenderer+ShareLink)
Step 5: Determine File Location
Check project structure:
- If
Sources/exists ->Sources/MilestoneCelebration/ - If
App/exists ->App/MilestoneCelebration/ - Otherwise ->
MilestoneCelebration/
Output Format
After generation, provide:
Files Created
MilestoneCelebration/
├── Milestone.swift # Model with threshold, icon, unlock state
├── MilestoneTracker.swift # @Observable tracker with persistence
├── ConfettiView.swift # CAEmitterLayer confetti animation
├── CelebrationOverlay.swift # Full-screen celebration overlay
├── MilestoneBadgeView.swift # Badge with locked/unlocked + progress ring
├── MilestoneCollectionView.swift # Grid of all milestones
├── HapticManager.swift # Celebration haptics (optional)
└── ShareableMilestoneCard.swift # Achievement share card (optional)
Integration Steps
Trigger celebration on threshold:
struct WorkoutCompleteView: View {
@State private var tracker = MilestoneTracker()
@State private var celebratingMilestone: Milestone?
var body: some View {
VStack {
// ... workout summary content ...
Button("Save Workout") {
saveWorkout()
let newCount = totalWorkouts + 1
if let milestone = tracker.checkThreshold(value: newCount, category: .workouts) {
celebratingMilestone = milestone
}
}
}
.overlay {
if let milestone = celebratingMilestone {
CelebrationOverlay(milestone: milestone) {
celebratingMilestone = nil
}
}
}
}
}
Badge collection screen:
struct ProfileView: View {
@State private var tracker = MilestoneTracker()
var body: some View {
NavigationStack {
ScrollView {
MilestoneCollectionView(
milestones: tracker.allMilestones,
columns: 3
)
}
.navigationTitle("Achievements")
}
}
}
Share an achievement:
struct MilestoneDetailView: View {
let milestone: Milestone
@State private var showShareCard = false
var body: some View {
VStack {
MilestoneBadgeView(milestone: milestone, size: .large)
Text(milestone.title).font(.title2.bold())
Text(milestone.milestoneDescription).foregroundStyle(.secondary)
if milestone.isUnlocked {
Button("Share Achievement") {
showShareCard = true
}
.buttonStyle(.borderedProminent)
}
}
.sheet(isPresented: $showShareCard) {
ShareableMilestoneCard(milestone: milestone, brandName: "FitApp")
}
}
}
Testing
@Test
func milestoneUnlocksAtThreshold() {
let tracker = MilestoneTracker(store: InMemoryMilestoneStore())
let milestone = Milestone(
id: "first-10",
title: "First 10",
milestoneDescription: "Complete 10 workouts",
threshold: 10,
iconName: "flame.fill"
)
tracker.register(milestone)
let result = tracker.checkThreshold(value: 10, for: milestone.id)
#expect(result != nil)
#expect(result?.isUnlocked == true)
#expect(result?.unlockedDate != nil)
}
@Test
func milestoneDoesNotUnlockBelowThreshold() {
let tracker = MilestoneTracker(store: InMemoryMilestoneStore())
let milestone = Milestone(
id: "first-10",
title: "First 10",
milestoneDescription: "Complete 10 workouts",
threshold: 10,
iconName: "flame.fill"
)
tracker.register(milestone)
let result = tracker.checkThreshold(value: 9, for: milestone.id)
#expect(result == nil)
}
@Test
func confettiRespectsReduceMotion() {
// When Reduce Motion is enabled, ConfettiView should not emit particles
let config = ConfettiConfiguration(reduceMotionOverride: true)
#expect(config.shouldAnimate == false)
}
@Test
func celebrationOverlayAutoDismisses() async throws {
// Verify overlay dismisses after the configured duration
let expectation = XCTestExpectation(description: "Dismissed")
let duration: TimeInterval = 0.5
// Test that the onDismiss callback fires after duration
}
Common Patterns
Trigger Celebration on Threshold
Best for: fitness, learning, productivity apps.
- Define milestones with numeric thresholds (10 workouts, 100 pages read, 7-day streak)
MilestoneTrackerchecks values against registered milestones- Celebration fires only on the first crossing — subsequent calls are no-ops
- Persist unlock state so celebrations do not repeat
Badge Collection View
Best for: gamification, loyalty, progression systems.
- Grid of all milestones: locked ones are dimmed/grayscale, unlocked ones are vibrant
- Progress ring shows how close user is to unlocking each badge
- Tapping a badge shows detail view with full description and share option
- Counter showing "12 of 20 unlocked" at the top
Share Achievement
Best for: social apps, fitness, competitions.
- Render the unlocked badge + title + metric as a shareable image
- Use
ImageRendererat@2xscale for Retina quality - Include app branding and optional QR code for deep linking
- Integrate with
ShareLinkfor native share sheet
Gotchas
CAEmitterLayer Performance on Older Devices
- Confetti with many particles (200+) can drop frames on A11 and older chips
- Cap
birthRateat 50-80 for smooth 60fps on most devices - Use
emitterCellswith only 3-5 distinct shapes to reduce GPU draw calls - Stop emission after burst duration — do not leave the emitter running indefinitely
- Profile with Instruments (Core Animation template) if users report frame drops
Reduce Motion Accessibility
- Always check
UIAccessibility.isReduceMotionEnabledbefore starting particle animations - When Reduce Motion is on: skip confetti, show a static badge reveal or a simple fade-in instead
- Use
@Environment(\.accessibilityReduceMotion)in SwiftUI for reactive updates - Provide an equally rewarding experience without animation — the badge reveal and message still appear
Haptic Feedback on Non-Supporting Devices
UINotificationFeedbackGeneratoris a no-op on devices without a Taptic Engine (e.g., iPod touch, iPad)- Always guard with
CHHapticEngine.capabilitiesForHardware().supportsHapticsbefore preparing generators - On macOS, haptics are not available — gate with
#if canImport(UIKit)and#if os(iOS) - Use
.sensoryFeedback(.success, trigger:)modifier on iOS 17+ for a simpler SwiftUI-native approach
Sound Effect Considerations
- Use
AudioServicesPlaySystemSoundfor lightweight celebration sounds (no audio session needed) - Respect the device silent/mute switch —
AudioServicesPlaySystemSoundhonors it automatically - For custom sounds, keep files under 30 seconds and use CAF or WAV format
- Do not play sounds when Reduce Motion is enabled (some users enable it for sensory reasons)
Persistence Edge Cases
- If the app is terminated between unlocking a milestone and persisting it, the user may see the celebration again
- Use
UserDefaultsfor simple unlock flags or a lightweight JSON file for full milestone state - For
SwiftDataorCoreDataapps, consider storing unlock state alongside the user's data model - Sync unlock state via CloudKit if the app supports multiple devices
References
- templates.md — All production Swift templates
- Related:
generators/streak-tracker— Streak tracking pairs naturally with milestone celebrations - Related:
generators/share-card— Shareable achievement card rendering and social sharing - Related:
generators/variable-rewards— Variable reward schedules for deeper engagement
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
legal
Legal document generation and compliance guidance for indie Apple developers. Covers privacy policies, terms of service, EULAs, GDPR/CCPA/DPDP compliance, and Apple App Store legal requirements. Use when user needs legal documents or compliance guidance.
privacy-policy
Generate privacy policies, terms of service, and EULAs for Apple platform apps. Detects data collection patterns, third-party SDKs, and generates region-specific legal documents with Apple Privacy Nutrition Label mapping. Use when user needs legal documents or data collection disclosure for App Store submission.
ios-development
Comprehensive iOS development guidance including Swift best practices, SwiftUI patterns, UI/UX review against HIG, and app planning. Use for iOS code review, best practices, accessibility audits, or planning new iOS apps.
assistive-access
Assistive Access implementation for cognitive accessibility including simplified scenes, navigation icons, runtime detection, and design principles. Use when optimizing apps for Assistive Access mode.
ui-review
Review SwiftUI code for iOS/watchOS Human Interface Guidelines compliance, font usage, Dynamic Type support, and accessibility. Use when user mentions UI review, HIG, accessibility audit, font checks, or wants to verify interface design against Apple standards.
navigation-patterns
SwiftUI navigation architecture patterns including NavigationStack, NavigationSplitView, TabView, programmatic navigation, and custom transitions. Use when reviewing or building navigation, fixing navigation bugs, or architecting app flow.
Didn't find tool you were looking for?