fix(ios): prevent crash when FlutterEngine is destroyed during background#625
Open
IsmailAshour wants to merge 1 commit intodlutton:masterfrom
Open
fix(ios): prevent crash when FlutterEngine is destroyed during background#625IsmailAshour wants to merge 1 commit intodlutton:masterfrom
IsmailAshour wants to merge 1 commit intodlutton:masterfrom
Conversation
…ound Add lifecycle guard to prevent NSInternalInconsistencyException "Sending a message before the FlutterEngine has been run" crash. The crash occurs when iOS tears down TTS resources (AVSpeechSynthesizer delegate callbacks) after the app enters background and the FlutterEngine is no longer running. The delegate methods unconditionally call channel.invokeMethod() which throws a fatal exception on a dead engine. Changes: - Add `isEngineAttached` flag to track engine lifecycle state - Add `safeInvokeMethod` helper that guards channel calls - Add `deinit` to stop synthesizer, nil delegate, and clear result callbacks before deallocation - Guard `didFinish` delegate with early return when engine is detached - Replace all 6 unguarded `channel.invokeMethod` calls with `safeInvokeMethod` Fixes dlutton#558 Fixes dlutton#595 Related: dlutton#318 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3142fb8 to
177bb07
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a FATAL crash on iOS when
AVSpeechSynthesizerDelegatecallbacks fire after theFlutterEnginehas been destroyed.Exception:
NSInternalInconsistencyException: Sending a message before the FlutterEngine has been run.Blame frame:
@objc SwiftFlutterTtsPlugin.__ivar_destroyerRoot Cause
When the app enters the background, iOS may tear down TTS resources asynchronously. The
AVSpeechSynthesizerDelegatemethods (didFinish,didStart,didCancel, etc.) unconditionally callself.channel.invokeMethod(...)— but theFlutterEnginemay no longer be running. This throws a fatalNSInternalInconsistencyException.Three distinct crash paths observed in production:
TTSSpeechServerInstance terminateSpeechThread→channel.invokeMethodAVAudioSession privatePostNotificationForType→channel.invokeMethodTTSSpeechServerInstance dealloc→AXSpeechManager .cxx_destruct→channel.invokeMethodAll crashes occur with
processState: BACKGROUND.Fix
isEngineAttachedflag to track engine lifecycle statesafeInvokeMethodhelper that guardschannel.invokeMethodcalls behind the flagdeinitto stop the synthesizer, nil the delegate, clear pending result callbacks, and setisEngineAttached = false— preventing delegate callbacks during deallocationdidFinishdelegate body with early return when engine is detached (protectsspeakResult/synthResultclosure calls too)channel.invokeMethodcalls withsafeInvokeMethodAffected Devices (from production Crashlytics)
Related Issues
Fixes #558
Fixes #595
Related: #318, flutter/flutter#104637