## Heartbeat (lub-dub loop)

- Use case: A health, meditation, or connection feature that plays a realistic looping heartbeat.
- Feel: A strong round "lub" and a softer "dub", repeating ~60 bpm. Low sharpness keeps it organic.
- APIs: CHHapticAdvancedPatternPlayer, loopEnabled
- Minimum OS: iOS 13+

### Core Haptics

```swift
import CoreHaptics

final class HeartbeatPlayer {
    private let engine: CHHapticEngine
    private var player: CHHapticAdvancedPatternPlayer?

    init?() {
        guard CHHapticEngine.capabilitiesForHardware().supportsHaptics,
              let engine = try? CHHapticEngine() else { return nil }
        self.engine = engine
        try? engine.start()
    }

    func start(bpm: Double = 60) throws {
        let beat = 60.0 / bpm
        func hit(at t: TimeInterval, intensity: Float) -> CHHapticEvent {
            CHHapticEvent(eventType: .hapticTransient, parameters: [
                CHHapticEventParameter(parameterID: .hapticIntensity, value: intensity),
                CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.15),
            ], relativeTime: t)
        }

        // lub ... dub ......... (loop)
        let events = [hit(at: 0, intensity: 1.0),
                      hit(at: 0.14, intensity: 0.6)]

        let pattern = try CHHapticPattern(events: events, parameters: [])
        player = try engine.makeAdvancedPlayer(with: pattern)
        player?.loopEnabled = true
        player?.loopEnd = beat          // loop length = one full beat
        try player?.start(atTime: CHHapticTimeImmediate)
    }

    func stop() {
        try? player?.stop(atTime: CHHapticTimeImmediate)
    }
}

// let heart = HeartbeatPlayer()
// try? heart?.start(bpm: 72)
```

### Notes
- loopEnabled + loopEnd on an advanced player is far more stable than restarting the pattern with a Timer.
- Drive bpm from live HealthKit heart-rate samples for a "feel someone's pulse" feature.
