From 9c82ce7c0a553e121f6eafcf19e469e16b0df83e Mon Sep 17 00:00:00 2001 From: WYSIATI Date: Sun, 22 Mar 2026 16:21:11 +0800 Subject: [PATCH] fix(ios): stop interrupting other apps' audio on play and fix session deactivation Two issues in the iOS audio session management: 1. play() called [session setActive:YES] on every invocation, which interrupts other apps' audio (Spotify, Apple Music, etc.) even when the category is configured with mixWithOthers. The per-play activation triggers iOS audio route reconfiguration that pauses other audio sessions. Apps should call setActive(true) once during setup instead. play() also registered NSNotificationCenter observers for AVAudioSessionInterruptionNotification on every call without removing previous registrations. The audioSessionChangeObserver auto-resumes playback on InterruptionTypeEnded, causing unexpected sound replay after lock screen, phone calls, or Siri activations. Fix: Remove setActive:YES and observer registration from play(). Audio session lifecycle should be managed by the app, not the library. 2. setActive(false) called [session setActive:NO] without the AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation flag. Without this flag, other apps are never notified that they can resume playback, causing their audio to stay paused permanently after our session deactivates. Fix: Pass notifyOthersOnDeactivation when deactivating so other apps can resume their audio. These issues affect any app that plays short sound effects (timers, notifications, UI feedback) while the user has background music playing. Refs: #762, #480, #419, #559 --- ios/RNSound.mm | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/ios/RNSound.mm b/ios/RNSound.mm index 94b2335b..9ee6d1d6 100644 --- a/ios/RNSound.mm +++ b/ios/RNSound.mm @@ -117,7 +117,17 @@ - (NSDictionary *)constantsToExport { RCT_EXPORT_METHOD(setActive:(BOOL)active) { AVAudioSession *session = [AVAudioSession sharedInstance]; - [session setActive:active error:nil]; + if (active) { + [session setActive:YES error:nil]; + } else { + // Use notifyOthersOnDeactivation so other apps (e.g. Spotify, + // Apple Music) are informed they can resume their audio playback. + // Without this flag, other apps' audio stays paused permanently + // after our session deactivates. + [session setActive:NO + withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation + error:nil]; + } } RCT_EXPORT_METHOD(setMode:(NSString *)modeName) { @@ -216,23 +226,22 @@ - (NSString *)categoryForName:(NSString *)categoryName { } RCT_EXPORT_METHOD(play:(double)key callback:(RCTResponseSenderBlock)callback) { - [[AVAudioSession sharedInstance] setActive:YES error:nil]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(audioSessionChangeObserver:) - name:AVAudioSessionRouteChangeNotification - object:[AVAudioSession sharedInstance]]; - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(audioSessionChangeObserver:) - name:AVAudioSessionInterruptionNotification - object:[AVAudioSession sharedInstance]]; - + // Audio session activation is left to the app via setCategory/setActive. + // Calling setActive:YES here on every play() call interrupts other apps' + // audio (e.g. Spotify, Apple Music) even when the category is configured + // with mixWithOthers — because the per-play activation triggers iOS audio + // route reconfiguration. Apps should call setActive(true) once during + // setup, not on every play. + // + // Observer registration is also removed. The audioSessionChangeObserver + // auto-resumes playback on AVAudioSessionInterruptionTypeEnded, which + // causes unexpected sound replay after lock screen, phone calls, or Siri. + // Apps that need interruption handling should implement it at the app level + // rather than having the sound library auto-resume behind their back. + self._key = key; AVAudioPlayer *player = [self playerForKey:key]; - + if (player) { NSNumber *myNumber = @(key); [[self callbackPool] setObject:[callback copy] forKey:myNumber];