iOS improvements and bug fixes - #1038
Merged
Merged
Conversation
Two callers were reaching for the same AVAudioSession at launch: SplashCoordinator.playSplashAudio() hard-coded .playback + .mixWithOthers and setActive(true), while IosAudioEngine.ensureEngineStarted() ran its own setCategory/setActive with options driven by the user's MIX_AUDIO preference (default false). Whichever landed second, the loser's AVAudioPlayer could stop delivering audioPlayerDidFinishPlaying. Because SplashCoordinator only dismisses when both didFinishAudio and the 1.5s timer have fired, a missed callback wedged the splash indefinitely; iOS watchdog-killed the app ~20% of launches and reported it as a crash on the next launch. Make IosAudioEngine the single owner of the session: expose configureAudioSession() and have the Swift splash call it before playing, then stop deactivating the session on dismiss (the engine is about to use it). Both paths now use identical category/options and there is one setCategory/setActive call site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
installUnhandledExceptionLogger() was called from inside the Compose entry point (MainViewController), which meant Kotlin/Native had no setUnhandledExceptionHook installed during LegacyMigrator, FirebaseBootstrap, and IosSoundscapeService construction. Any coroutine failure in that window terminated the process with nothing written to stderr. Make the installer public and move the call to iOSApp.init() as the first line, so the hook is active before any Swift-invoked Kotlin code runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Opening the search popup raises the IME, which used to hide the top and bottom bars on the home screen. With no top bar, the collapsed search bar shifted up under the transparent iOS status bar and stayed visible there (the popup does not paint over the status-bar strip). Dismissing the popup produced the reverse glitch: `expanded` cleared immediately, but the IME took its own animation to close, briefly hiding the bars again. Hold a `searchOwnsIme` flag true from the moment the popup opens the IME until the IME has fully closed. Keep the top/bottom bars in place for the whole search session, so the underlying layout never moves and nothing bleeds behind the status bar. Once the top bar stays visible on Android too, the search Popup would open below it (Popup defaults to its anchor position). Pin the Popup with a PopupPositionProvider that always returns (0, 0) so it covers the whole window on both platforms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The home-screen callout buttons (my location, what's around me, ahead of me, nearby markers) queued audio on iOS instead of cancelling in-flight callouts the way Android does. Port Android's startCallout pattern - a coroutine job that cancels its predecessor and clears the audio queue before speaking - and match Android's ClearQueue semantics in IosAudioEngine so earcons queued behind an interrupted callout are dropped instead of playing out. Run the callout coroutine on Dispatchers.Main so its AVAudioEngine mutations don't race with the TTS render callback, and guard the render callback so a late-arriving render for a cancelled sound doesn't attach a zombie node. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The four home-screen callout methods were passing addModeEarcon=false to speakCalloutCommon, so iOS never played the enter/exit beeps that frame each callout on Android. myLocation now plays the enter earcon immediately (feedback while the reverse geocoder runs) and the exit earcon after the callout; whatsAroundMe/aheadOfMe/nearbyMarkers now pass addModeEarcon=true. Callouts with no positioned strings or a null result skip the earcons so a nothing-to-say press stays silent instead of just beeping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tapping the search bar for the first time while a callout was speaking caused TTS to crackle and gap out, because iOS's first soft-keyboard instantiation stalls the main thread for hundreds of milliseconds and the audio pipeline (TTS render completion, DiscretePlayer completion, startCallout coroutine) was hopping through the main queue between utterances. Introduce a serial GCD queue owned by IosAudioEngine and expose it as a CoroutineDispatcher, then route the two render-callback dispatches and startCallout onto that queue instead of Dispatchers.Main. AVAudioEngine mutations stay serialized (so the `[_nodes containsObject: node]` crash the previous commits fixed doesn't return), and callout audio is now decoupled from any main-thread stall. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Legacy iOS animated whichever "hear my surroundings" button was speaking and stopped animating when the callout finished or was cancelled. Port that: MediaControllableService gains an activeCalloutFlow<TourButton?>, each service's startCallout publishes/clears the source around its body (with a compareAndSet in finally so a superseding callout keeps its value), HomeViewModel folds the flow into HomeState, and the home bottom bar flips the active button to the theme primary colour with a pulsing icon scale until the flow returns to null. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
No description provided.