iOS SDK
The Pulsar iOS SDK provides haptic feedback through three building blocks: Presets, PatternComposer, and RealtimeComposer. All are accessed through the main Pulsar class.
Requirements
Section titled “Requirements”- iOS 13.0+
- Swift 5.9+
Installation
Section titled “Installation”Latest available version: 1.4.0
Add Pulsar as a Swift Package dependency in Xcode:
- Go to File > Add Package Dependencies…
- Enter the repository URL
- Select the Pulsar library product
Or add it to your Package.swift:
dependencies: [ .package(url: "https://github.com/software-mansion-labs/pulsar-ios", from: "1.4.0")]Create a Pulsar instance to access all SDK features:
import Pulsar
let pulsar = Pulsar()Presets
Section titled “Presets”A collection of ready-to-use haptic patterns accessed through pulsar.getPresets().
let presets = pulsar.getPresets()Built-in presets
Section titled “Built-in presets”| Method | Description |
|---|---|
afterglow() | A three-beat phrase that dissolves gently, ideal for soft endings or gradually quieting feedback. |
aftershock() | A firm opening that settles calmly, ideal for transitions needing a strong start and a gentle finish. |
alarm() | Relentless and urgent, best for critical errors or emergencies that require immediate attention. |
anvil() | The full weight of a massive collision, conveys sheer physical force and momentum. |
applause() | A growing wave of appreciation, ideal for celebratory moments or social approval. |
Example
let presets = pulsar.getPresets()
presets.dogBark()System presets
Section titled “System presets”Plays the platform’s platform haptic feedback presets.
| Method | Description |
|---|---|
systemImpactHeavy() | UIImpactFeedbackGenerator.heavy |
systemImpactLight() | UIImpactFeedbackGenerator.light |
systemImpactMedium() | UIImpactFeedbackGenerator.medium |
systemImpactRigid() | UIImpactFeedbackGenerator.rigid |
systemImpactSoft() | UIImpactFeedbackGenerator.soft |
Example
let presets = pulsar.getPresets()
presets.systemImpactHeavy()Playing by name
Section titled “Playing by name”You can also play a preset by its string name using getByName(_:). This returns a Preset? that you call play() on.
Available names:
AfterglowAftershockAlarmAnvilApplauseAscentBalloonPopBarrage
Example
let presets = pulsar.getPresets()
// Direct callpresets.dogBark()
// By namepresets.getByName("Success")?.play()PatternComposer
Section titled “PatternComposer”Composes and plays custom haptic patterns. Get a new instance from pulsar.getPatternComposer(). Each call returns a fresh composer, so you can manage multiple patterns independently.
let composer = pulsar.getPatternComposer()Methods
Section titled “Methods”parsePattern(hapticsData:)
Section titled “parsePattern(hapticsData:)”Parses a PatternData object and prepares it for playback.
func parsePattern(hapticsData: PatternData)parsePatternWithSound(hapticsData:uri:volume:offset:start:duration:)
Section titled “parsePatternWithSound(hapticsData:uri:volume:offset:start:duration:)”Parses a pattern together with a short sound played in sync with the haptics. The sound is registered as a Core Haptics audio event on the same timeline as the pattern, so audio and haptics stay sample-accurate. volume (0–1) and offset (milliseconds, shifting the audio relative to the haptics) are optional.
func parsePatternWithSound( hapticsData: PatternData, uri: String, volume: Float = 1, offset: Double = 0, start: Double = 0, duration: Double = 0)uri must resolve to a local file — an absolute path, a file:// URL, or the name of a resource bundled in the app. When no extension is given it defaults to .wav, so a bare name like "beep" resolves beep.wav from the app bundle (add the file to your target’s Copy Bundle Resources). Requires a real device — Core Haptics is unavailable on the simulator — and a missing or unregisterable file degrades gracefully to haptics-only.
start and duration (both in milliseconds) trim the clip: start is where playback begins in the source file, duration is how much of it to play (0 plays to the end). Core Haptics registers an audio resource by URL only, so when either is set the file is sliced to a temporary .caf first — the temp file is cleaned up on the next parse and on dispose.
playPattern(hapticsData:)
Section titled “playPattern(hapticsData:)”Parses and immediately plays a pattern. Equivalent to calling parsePattern followed by play.
func playPattern(hapticsData: PatternData)play()
Section titled “play()”Plays the previously parsed pattern.
func play()stop()
Section titled “stop()”Stops all playback.
func stop()Example
Section titled “Example”let composer = pulsar.getPatternComposer()
let pattern = PatternData( continuousPattern: ContinuousPattern( amplitude: [ ValuePoint(time: 0, value: 0), ValuePoint(time: 200, value: 1), ValuePoint(time: 400, value: 0), ], frequency: [ ValuePoint(time: 0, value: 0.3), ValuePoint(time: 400, value: 0.8), ] ), discretePattern: [ DiscretePoint(time: 0, amplitude: 1, frequency: 0.5), DiscretePoint(time: 100, amplitude: 0.5, frequency: 0.5), ])
composer.playPattern(hapticsData: pattern)RealtimeComposer
Section titled “RealtimeComposer”Provides real-time haptic control with live amplitude and frequency modulation. Useful for gesture-driven or continuously evolving haptic experiences. Get the shared instance from pulsar.getRealtimeComposer().
let realtime = pulsar.getRealtimeComposer()Methods
Section titled “Methods”set(amplitude:frequency:)
Section titled “set(amplitude:frequency:)”Updates the ongoing haptic with new amplitude and frequency values. Automatically starts playback if it is not already active. Values are clamped to the 0-1 range.
func set(amplitude: Float, frequency: Float)playDiscrete(amplitude:frequency:)
Section titled “playDiscrete(amplitude:frequency:)”Plays a single discrete haptic event.
func playDiscrete(amplitude: Float = 1.0, frequency: Float = 0.5)stop()
Section titled “stop()”Stops the active continuous haptic.
func stop()Properties
Section titled “Properties”isActive
Section titled “isActive”Returns true if a continuous haptic is currently playing.
var isActive: Bool { get }Example
Section titled “Example”let realtime = pulsar.getRealtimeComposer()
// Start a continuous hapticrealtime.set(amplitude: 0.5, frequency: 0.8)
// Update parameters over timerealtime.set(amplitude: 1.0, frequency: 0.3)
// Play a one-off discrete eventrealtime.playDiscrete(amplitude: 0.7, frequency: 0.5)
// Stoprealtime.stop()Preset bundles
Section titled “Preset bundles”A .pulsar bundle exported from Pulsar Studio holds your own presets — each a haptic pattern with
optional synced audio and animation. pulsar-gen turns a bundle into a typed accessor, so every
preset is a real member: a renamed preset becomes a compile error rather than a silent runtime
miss.
Generate the accessor
Section titled “Generate the accessor”Add the .pulsar to your app target (Copy Bundle Resources), then either apply the SwiftPM build
plugin so the accessor regenerates on every build:
.target( name: "MyApp", plugins: [.plugin(name: "PulsarGenPlugin", package: "Pulsar")])…or run the CLI once and commit the result — the practical option for an Xcode app target:
npx @swmansion/pulsar-gen acme-pack.pulsar --target swift --out Sources/MyApp/Load and play
Section titled “Load and play”let pulsar = Pulsar()let bundle = try pulsar.loadBundle(AcmePack.descriptor, strict: true)
bundle.heartbeatV2.play()bundle.explosion.stop()The descriptor finds the .pulsar in the app’s main bundle. strict: true checks the shipped
bundle against the generated types, failing loudly on a stale copy.
Each PresetHandle exposes id, duration, play(), stop(), and animation — the Lottie bytes
and timing for your own animation view. Pulsar carries and time-aligns the animation; the app
renders it:
if let animation = bundle.heartbeatV2.animation { myLottieView.load(data: animation.data)}Audio authored into a preset is registered as a Core Haptics audio event, so it shares the engine clock with the haptics.
For an id only known at runtime, bundle.get(id) returns a handle or nil. Call bundle.dispose()
to release the native patterns when you are done.
See iOS/PulsarApp for a
working screen.
Settings
Section titled “Settings”Configuration methods available directly on the Pulsar instance.
| Method | Description |
|---|---|
enableHaptics(state: Bool) | Enable or disable all haptic feedback |
enableSound(state: Bool) | Enable or disable audio simulation |
enableCache(state: Bool) | Enable or disable preset caching |
clearCache() | Clear the preset cache |
preloadPresets(presetNames: [String]) | Preload presets by name for faster playback |
stopHaptics() | Stop all currently playing haptics |
shutDownEngine() | Shut down the haptic engine |
isHapticsSupported() -> Bool | Check if the device supports haptics |
isHapticsEnabled: Bool | Returns whether haptic feedback is currently enabled |
Example
Section titled “Example”let pulsar = Pulsar()
// Preload frequently used presetspulsar.preloadPresets(presetNames: ["Earthquake", "Success"])
// Disable haptics temporarilypulsar.enableHaptics(state: false)
// Check device supportif pulsar.isHapticsSupported() { pulsar.getPresets().success()}PatternData
Section titled “PatternData”Describes a complete haptic pattern with discrete pulses and continuous envelope curves.
class PatternData: NSObject, Codable { let continuousPattern: ContinuousPattern let discretePattern: [DiscretePoint]
init( continuousPattern: ContinuousPattern, discretePattern: [DiscretePoint] )}ContinuousPattern
Section titled “ContinuousPattern”Represents continuous haptic curves for amplitude and frequency.
class ContinuousPattern: NSObject, Codable { let amplitude: [ValuePoint] let frequency: [ValuePoint]
init(amplitude: [ValuePoint], frequency: [ValuePoint])}ValuePoint
Section titled “ValuePoint”A single point in a continuous curve.
class ValuePoint: NSObject, Codable { let time: Double // Milliseconds from pattern start let value: Float // Normalized value (0-1)
init(time: Double, value: Float)}DiscretePoint
Section titled “DiscretePoint”A single discrete haptic event.
class DiscretePoint: NSObject, Codable { let time: Double // Milliseconds from pattern start let amplitude: Float // Intensity (0-1) let frequency: Float // Sharpness (0-1)
init(time: Double, amplitude: Float, frequency: Float)}Use discretePattern for distinct taps and impacts. Use continuousPattern envelopes to shape a sustained haptic over time.
Preset
Section titled “Preset”The protocol that all preset implementations conform to.
protocol Preset { func play() static func getInstance(haptics: Pulsar) -> Preset static var name: String { get }}