Skip to content

Commit 4810b2a

Browse files
committed
fix: continuous mode not working after putting app in background
1 parent 0d8fc5c commit 4810b2a

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

ios/AhapUtils.swift

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@ protocol HapticAnimationState {
2121
var hapticCurves: [CHHapticParameterCurve] { get }
2222
}
2323

24+
/// Stores the configuration for a continuous player so it can be recreated after engine reinitialization
25+
struct ContinuousPlayerConfig {
26+
let playerId: String
27+
let initialIntensity: Float
28+
let initialSharpness: Float
29+
}
30+
2431
class HapticFeedback {
2532
static let shared = HapticFeedback()
2633

2734
private var engine: CHHapticEngine?
2835
private var hapticPlayers: [String: CHHapticAdvancedPatternPlayer] = [:]
2936
private var continuousPlayers: [String: CHHapticAdvancedPatternPlayer] = [:]
37+
/// Stores configurations for continuous players so they can be recreated after engine reinitialization
38+
private var continuousPlayerConfigs: [String: ContinuousPlayerConfig] = [:]
3039
private let lock = NSLock()
3140

3241
public var supportsHaptics: Bool {
@@ -40,7 +49,6 @@ class HapticFeedback {
4049
}
4150

4251
lock.lock()
43-
defer { lock.unlock() }
4452

4553
// Clean up existing engine first
4654
if engine != nil {
@@ -53,6 +61,7 @@ class HapticFeedback {
5361
engine = try CHHapticEngine()
5462
} catch let error {
5563
print("Engine Creation Error: \(error)")
64+
lock.unlock()
5665
return
5766
}
5867

@@ -98,6 +107,19 @@ class HapticFeedback {
98107
} catch {
99108
print("Failed to start the engine: \(error)")
100109
}
110+
111+
// Copy configs while holding the lock, then release before recreating players
112+
let configsCopy = Array(continuousPlayerConfigs.values)
113+
lock.unlock()
114+
115+
// Recreate any continuous players that were registered before engine was destroyed
116+
for config in configsCopy {
117+
createContinuousPlayerInternal(
118+
playerId: config.playerId,
119+
initialIntensity: config.initialIntensity,
120+
initialSharpness: config.initialSharpness
121+
)
122+
}
101123
}
102124

103125
public func createHapticPlayers<State: HapticAnimationState>(for states: [State]) {
@@ -194,7 +216,28 @@ class HapticFeedback {
194216
/// Creates a continuous haptic player with the given ID.
195217
/// - If a player with this ID already exists, it will be stopped and replaced.
196218
/// - If the engine is not initialized or device doesn't support haptics, this is a no-op.
219+
/// - The player configuration is stored so it can be recreated after engine reinitialization.
197220
public func createContinuousPlayer(playerId: String, initialIntensity: Float, initialSharpness: Float) {
221+
// Store the configuration so we can recreate the player after engine reinitialization
222+
let config = ContinuousPlayerConfig(
223+
playerId: playerId,
224+
initialIntensity: initialIntensity,
225+
initialSharpness: initialSharpness
226+
)
227+
228+
lock.lock()
229+
continuousPlayerConfigs[playerId] = config
230+
lock.unlock()
231+
232+
createContinuousPlayerInternal(
233+
playerId: playerId,
234+
initialIntensity: initialIntensity,
235+
initialSharpness: initialSharpness
236+
)
237+
}
238+
239+
/// Internal method to create a continuous player without storing config (used during recreation)
240+
private func createContinuousPlayerInternal(playerId: String, initialIntensity: Float, initialSharpness: Float) {
198241
lock.lock()
199242
defer { lock.unlock() }
200243

@@ -311,10 +354,14 @@ class HapticFeedback {
311354
/// Destroys the continuous haptic player and releases resources.
312355
/// - If the player doesn't exist (not created or already destroyed), this is a safe no-op.
313356
/// - The player will be stopped if it's currently playing.
357+
/// - The player configuration is also removed, so it won't be recreated after engine reinitialization.
314358
public func destroyContinuousPlayer(playerId: String) {
315359
lock.lock()
316360
defer { lock.unlock() }
317361

362+
// Remove the stored config so this player won't be recreated after engine reinitialization
363+
continuousPlayerConfigs.removeValue(forKey: playerId)
364+
318365
guard let player = continuousPlayers[playerId] else {
319366
// Player doesn't exist - safe no-op
320367
// This can happen if destroy is called before create, or called multiple times

0 commit comments

Comments
 (0)