From 2f8a52742b1b9b25d6d594926dded8d49ff7ef3f Mon Sep 17 00:00:00 2001 From: lavigarg-simform Date: Fri, 19 Jun 2026 19:09:24 +0530 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Prevent=20`nullptr=20=3D?= =?UTF-8?q?=3D=20Tap()`=20crash=20on=20iOS=20during=20recorder=20stream=20?= =?UTF-8?q?start?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + ios/Classes/RecorderBytesStreamEngine.swift | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3beb9051..dd827741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.1.0 (#unreleased) - Feature [#468](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/pull/468) - Add macOS support +- Fixed [#487](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues/487) - iOS crash `nullptr == Tap()` when starting bytes streaming with a stale/leaked input tap ## 2.0.2 diff --git a/ios/Classes/RecorderBytesStreamEngine.swift b/ios/Classes/RecorderBytesStreamEngine.swift index 68841c73..be34c6c6 100644 --- a/ios/Classes/RecorderBytesStreamEngine.swift +++ b/ios/Classes/RecorderBytesStreamEngine.swift @@ -21,6 +21,17 @@ class RecorderBytesStreamEngine { func attach(result: @escaping FlutterResult, sampleRate: Int) { let inputNode = audioEngine.inputNode + + // Reset any previous engine/tap state before installing a new tap. + // Installing a tap on a bus that already has one throws a fatal + // `nullptr == Tap()` exception (see issue #487). This also cleans up + // a tap leaked when a prior `audioEngine.start()` failed. + if audioEngine.isRunning { + audioEngine.stop() + } + inputNode.removeTap(onBus: 0) + totalFrames = 0 + inputNode.installTap(onBus: 0, bufferSize: 1024, format: nil) { (buffer, time) in if self.paused { return @@ -40,6 +51,9 @@ class RecorderBytesStreamEngine { do { try audioEngine.start() } catch { + // Remove the tap so a failed start doesn't leak it onto the bus, + // which would crash the next `attach` with `nullptr == Tap()`. + inputNode.removeTap(onBus: 0) result(FlutterError(code: Constants.audioWaveforms, message: "Error starting Audio Engine", details: error.localizedDescription)) } }