From 19946959148963729b0182d34b0d1049ac557c44 Mon Sep 17 00:00:00 2001 From: Sylvain BOILY Date: Sat, 17 Oct 2026 16:45:41 -0400 Subject: [PATCH] fix(ios): apply the setup() options to CallKit right away When the module loads, it builds the CallKit provider from the settings stored by setup() during the previous launch. setup() then stored the new options but left the provider alone (initCallKitProvider only creates it), so CallKit kept the previous launch's options, and a second setup() never changed them. setup() now sets the provider's configuration when the provider exists. The Mac Catalyst harness, which compiles RNCallKeep.m and reads callKeepProvider.configuration, shows it with options A stored and setup(B) (relaunch), and with setup(A) then setup(B): master keeps A, this change applies B. As documented, setup() from JavaScript is still ignored after a setup from native code; calling [RNCallKeep setup:] again now updates the options too (it went through the JavaScript method, which returned early). Based on #732 by Christian Schmidt, which applied the configuration the same way but also let JavaScript override a native setup. Co-authored-by: Christian Schmidt --- CHANGELOG.md | 1 + docs/api.md | 2 +- docs/setup.md | 4 +++- ios/RNCallKeep/RNCallKeep.m | 19 +++++++++++++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c0460e7..a431c235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ are not listed here. ### Fixed +- **iOS:** `setup()` applies its options to CallKit right away. When the app had run before, CallKit kept the options of the previous launch (the module builds the CallKit provider from the stored settings when it loads), and calling `setup()` again didn't change them either. An app that sets up from native code can now call `[RNCallKeep setup:]` again to change them; `setup()` from JavaScript is still ignored then, as documented. ([#732](https://github.com/react-native-webrtc/react-native-callkeep/pull/732), @c960657) - **Android:** `checkIsInManagedCall()`, `setAudioRoute()` and `toggleAudioRouteSpeaker()` no longer crash the app on Android 7 with `NoSuchMethodError`: they called Android 8 APIs. Before Android 8, `checkIsInManagedCall()` reports any ongoing call (there are no self-managed calls then), `setAudioRoute()` rejects and `toggleAudioRouteSpeaker()` does nothing. - **Android:** `setAudioRoute()` rejects when no call has the given UUID. Its promise never settled. - **Android:** Android lint passes on the library ([#646](https://github.com/react-native-webrtc/react-native-callkeep/issues/646)): `startCall` and `checkDefaultPhoneAccount` handle a `SecurityException` from Telecom (a permission can be revoked at any time), `endCall` uses `AudioManager.MODE_NORMAL`, and the library's `build.gradle` no longer declares the Android Gradle Plugin 3.0.1: the app's plugin builds it. ([#684](https://github.com/react-native-webrtc/react-native-callkeep/pull/684), @ramijarrar) diff --git a/docs/api.md b/docs/api.md index f6f9e222..dcd187ca 100644 --- a/docs/api.md +++ b/docs/api.md @@ -60,7 +60,7 @@ import RNCallKeep from 'react-native-callkeep'; Stores the settings without the rest of `setup()`, which already calls it. It takes the same options as [`setup()`](setup.md#options) and keeps the `ios` or `android` part for the current platform. -On iOS, CallKit's configuration (`appName`, `imageName`, `ringtoneSound`, `supportsVideo`...) is read once, when `setup()` creates the CallKit provider: a later `setSettings()` doesn't change it. +On iOS, `setSettings()` doesn't change CallKit's configuration (`imageName`, `ringtoneSound`, `supportsVideo`...): call [`setup()`](setup.md) again for that. ```js RNCallKeep.setSettings(options); diff --git a/docs/setup.md b/docs/setup.md index f5b61ee5..b826747e 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -92,11 +92,13 @@ RNCallKeep.setup(options).then(accepted => {}); `setup` calls internally `registerPhoneAccount`, `registerEvents` and `setSettings`. +You can call `setup()` again to change the options, for example when the user changes a preference. On iOS the new CallKit options (`includesCallsInRecents`, `supportsVideo`, `ringtoneSound`...) apply to the next calls. If you [set up from native code](#setup-from-native-code-ios), call `[RNCallKeep setup:]` again instead. + You can alternatively just call `setSettings()` with the same option as `setup()` to define only your settings. ## Setup from native code (iOS) -On iOS you can also perform setup in `AppDelegate`. Doing this allows capturing events prior to the react native event bridge being up. Please be aware that calling setup in `AppDelegate.m` will ignore any subsequent calls to `RNCallKeep.setup();`. +On iOS you can also perform setup in `AppDelegate`. Doing this allows capturing events prior to the react native event bridge being up. Please be aware that calling setup in `AppDelegate.m` will ignore any subsequent calls to `RNCallKeep.setup();` from JavaScript: to change the options, call `[RNCallKeep setup:]` again. ```objective-c @implementation AppDelegate diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 63288c28..cba39437 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -189,6 +189,16 @@ + (void)initCallKitProvider { } } +// Applies the stored settings to the CallKit provider. The provider may exist already: created when the +// module loaded, from the settings stored during the previous launch, or by an earlier setup(). ++ (void)configureCallKitProvider { + if (sharedProvider == nil) { + [self initCallKitProvider]; + } else { + sharedProvider.configuration = [RNCallKeep getProviderConfiguration:[self getSettings]]; + } +} + + (NSString *) getAudioOutput { @try{ NSArray* outputs = [AVAudioSession sharedInstance].currentRoute.outputs; @@ -204,7 +214,7 @@ + (NSString *) getAudioOutput { + (void)setup:(NSDictionary *)options { RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil]; - [callKeep setup:options]; + [callKeep setupWithOptions:options]; isSetupNatively = YES; } @@ -218,6 +228,11 @@ + (void)setup:(NSDictionary *)options { return; } + [self setupWithOptions:options]; +} + +- (void)setupWithOptions:(NSDictionary *)options +{ #ifdef DEBUG NSLog(@"[RNCallKeep][setup] options = %@", options); #endif @@ -226,7 +241,7 @@ + (void)setup:(NSDictionary *)options { [self setSettings: options]; - [RNCallKeep initCallKitProvider]; + [RNCallKeep configureCallKitProvider]; self.callKeepProvider = sharedProvider; [self.callKeepProvider setDelegate:self queue:nil];