diff --git a/iOS/Pulsar/Sources/Pulsar/Composers/PatternComposer.swift b/iOS/Pulsar/Sources/Pulsar/Composers/PatternComposer.swift index 825f2804..13063886 100644 --- a/iOS/Pulsar/Sources/Pulsar/Composers/PatternComposer.swift +++ b/iOS/Pulsar/Sources/Pulsar/Composers/PatternComposer.swift @@ -52,6 +52,7 @@ public class PatternComposer: NSObject { } private func parse(hapticsData: PatternData, audioEvent: CHHapticEvent?) { + releasePlayers() discreteLine.reset() continuousLine.reset() hasSound = audioEvent != nil @@ -162,6 +163,15 @@ public class PatternComposer: NSObject { } } + private func releasePlayers() { + if let id = continuousPlayerId { engine.removePlayer(id: id) } + if let id = discretePlayerId { engine.removePlayer(id: id) } + continuousPlayerId = nil + discretePlayerId = nil + continuousPattern = nil + discretePattern = nil + } + private func releaseAudio() { if let id = audioResourceID { engine.unregisterAudioResource(id) @@ -202,12 +212,7 @@ public class PatternComposer: NSObject { @objc public func dispose() { stop() - if let id = continuousPlayerId { engine.removePlayer(id: id) } - if let id = discretePlayerId { engine.removePlayer(id: id) } - continuousPlayerId = nil - discretePlayerId = nil - continuousPattern = nil - discretePattern = nil + releasePlayers() audioBuffer = nil hasSound = false releaseAudio() diff --git a/iOS/Pulsar/Sources/Pulsar/HapticEngineWrapper.swift b/iOS/Pulsar/Sources/Pulsar/HapticEngineWrapper.swift index 5e4bbbe0..6aeb4c6f 100644 --- a/iOS/Pulsar/Sources/Pulsar/HapticEngineWrapper.swift +++ b/iOS/Pulsar/Sources/Pulsar/HapticEngineWrapper.swift @@ -122,6 +122,7 @@ public extension HapticEngineWrapper { func stopPlayer(id: Int) { guard let player = playerRegistry[id] else { return } stopPlayer(player, errorPrefix: "Error stopping player") + unregisterPlayer(id) } func removePlayer(id: Int) { @@ -129,8 +130,7 @@ public extension HapticEngineWrapper { try? player.stop(atTime: 0) } - playerRegistry.removeValue(forKey: id) - playerCreationOrder.removeAll { $0 == id } + unregisterPlayer(id) } } @@ -350,6 +350,11 @@ private extension HapticEngineWrapper { playerCreationOrder.append(id) } + func unregisterPlayer(_ id: Int) { + playerRegistry.removeValue(forKey: id) + playerCreationOrder.removeAll { $0 == id } + } + func evictOldestPlayerIfNeeded() { guard playerRegistry.count >= playerLimit else { return } diff --git a/iOS/Pulsar/Tests/PulsarTests/CoreHapticsMockTests.swift b/iOS/Pulsar/Tests/PulsarTests/CoreHapticsMockTests.swift index 2dab27db..f9b184cc 100644 --- a/iOS/Pulsar/Tests/PulsarTests/CoreHapticsMockTests.swift +++ b/iOS/Pulsar/Tests/PulsarTests/CoreHapticsMockTests.swift @@ -67,6 +67,24 @@ struct CoreHapticsMockTests { #expect(HapticMockRecorder.shared.playerStops >= 1) } + @Test func playingAfterAStopBuildsAFreshPlayer() { + CoreHapticsMock.install() + defer { CoreHapticsMock.uninstall() } + + let engine = activeEngine() + let pattern = makePattern() + let created = engine.createPlayer(pattern: pattern) + #expect(created != nil) + guard let id = created else { return } + + engine.playPlayer(id: id, pattern: pattern) + engine.stopPlayer(id: id) + engine.playPlayer(id: id, pattern: pattern) + + #expect(HapticMockRecorder.shared.playersCreated == 2) + #expect(HapticMockRecorder.shared.playerStarts == 2) + } + @Test func stopHapticsStopsEveryRegisteredPlayer() { CoreHapticsMock.install() defer { CoreHapticsMock.uninstall() }