diff --git a/android/build.gradle b/android/build.gradle index 461defa9..24e9a759 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -80,7 +80,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "io.qonversion:sandwich:7.11.0" + implementation "io.qonversion:sandwich:7.12.0" } if (isNewArchitectureEnabled()) { diff --git a/android/src/main/java/com/qonversion/reactnativesdk/QonversionModule.kt b/android/src/main/java/com/qonversion/reactnativesdk/QonversionModule.kt index 6b7d0ac6..d6dadc76 100644 --- a/android/src/main/java/com/qonversion/reactnativesdk/QonversionModule.kt +++ b/android/src/main/java/com/qonversion/reactnativesdk/QonversionModule.kt @@ -246,6 +246,11 @@ class QonversionModule(reactContext: ReactApplicationContext) : NativeQonversion ) } + @ReactMethod + override fun invalidateRemoteConfigsCache() { + qonversionSandwich.invalidateRemoteConfigsCache() + } + @ReactMethod override fun attachUserToExperiment(experimentId: String, groupId: String, promise: Promise) { qonversionSandwich.attachUserToExperiment(experimentId, groupId, getResultListener(promise)) diff --git a/ios/RNQonversion.mm b/ios/RNQonversion.mm index b135cc12..c991decb 100644 --- a/ios/RNQonversion.mm +++ b/ios/RNQonversion.mm @@ -258,6 +258,14 @@ - (void)remoteConfigListForContextKeys:(NSArray *)contextKeys includ } } +- (void)invalidateRemoteConfigsCache { + @try { + [self.impl invalidateRemoteConfigsCache]; + } @catch (NSException *exception) { + QNR_LOG_EXCEPTION("invalidateRemoteConfigsCache", exception); + } +} + - (void)attachUserToExperiment:(NSString *)experimentId groupId:(NSString *)groupId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @try { [self.impl attachUserToExperiment:experimentId groupId:groupId resolve:resolve reject:reject]; diff --git a/ios/RNQonversionImpl.swift b/ios/RNQonversionImpl.swift index 02c6b620..9a469f63 100644 --- a/ios/RNQonversionImpl.swift +++ b/ios/RNQonversionImpl.swift @@ -196,6 +196,11 @@ public class RNQonversionImpl: NSObject { } } + @objc + public func invalidateRemoteConfigsCache() { + qonversionSandwich?.invalidateRemoteConfigsCache() + } + @objc public func attachUserToExperiment(_ experimentId: String, groupId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { qonversionSandwich?.attachUserToExperiment(with: experimentId, groupId: groupId) { result, error in diff --git a/qonversion-react-native-sdk.podspec b/qonversion-react-native-sdk.podspec index e89e2698..2e463cf7 100644 --- a/qonversion-react-native-sdk.podspec +++ b/qonversion-react-native-sdk.podspec @@ -16,6 +16,6 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,mm,cpp,swift}" s.private_header_files = "ios/**/*.h" - s.dependency "QonversionSandwich", "7.11.0" + s.dependency "QonversionSandwich", "7.12.0" install_modules_dependencies(s) end diff --git a/src/QonversionApi.ts b/src/QonversionApi.ts index c4df2bbd..663c6efb 100644 --- a/src/QonversionApi.ts +++ b/src/QonversionApi.ts @@ -324,6 +324,31 @@ export interface QonversionApi { */ remoteConfigListForContextKeys(contextKeys: string[], includeEmptyContextKey: boolean): Promise + /** + * Invalidates the cache of remote configs so the next {@link remoteConfig} or + * {@link remoteConfigList} call fetches a fresh targeting evaluation from the + * server instead of returning the cached copy. + * + * This method performs no network request itself — it only marks the cached + * values as stale. An in-flight remoteConfig load is re-issued once so its + * waiting calls receive a fresh evaluation; an in-flight remoteConfigList + * completes with the evaluation it started with. + * + * Call it when the targeting inputs changed and you need the change reflected + * immediately, for example after setting a batch of user properties your + * remote config targeting depends on. You do NOT need to call it after + * {@link identify} — the SDK invalidates the cache on identity changes + * automatically. + * + * If the re-issued load fails, the previously received evaluation is + * delivered instead of an error — the call never degrades below the + * pre-invalidation result. + * + * Call it after {@link Qonversion.initialize}: calling before initialization + * throws on Android and does nothing on iOS. + */ + invalidateRemoteConfigsCache(): void; + /** * This function should be used for the test purposes only. Do not forget to delete the usage of this function before the release. * Use this function to attach the user to the experiment. diff --git a/src/internal/QonversionInternal.ts b/src/internal/QonversionInternal.ts index f4599a45..ed855372 100644 --- a/src/internal/QonversionInternal.ts +++ b/src/internal/QonversionInternal.ts @@ -376,6 +376,10 @@ export default class QonversionInternal implements QonversionApi { return mappedRemoteConfigList; } + invalidateRemoteConfigsCache() { + RNQonversion.invalidateRemoteConfigsCache(); + } + async attachUserToExperiment(experimentId: string, groupId: string): Promise { await RNQonversion.attachUserToExperiment(experimentId, groupId); return; diff --git a/src/internal/__tests__/QonversionInternal.test.ts b/src/internal/__tests__/QonversionInternal.test.ts index 3cf51bf3..c989da4e 100644 --- a/src/internal/__tests__/QonversionInternal.test.ts +++ b/src/internal/__tests__/QonversionInternal.test.ts @@ -25,6 +25,7 @@ jest.mock('../specs/NativeQonversionModule', () => ({ }), onPromoPurchaseReceived: jest.fn(), purchaseWithResult: jest.fn(), + invalidateRemoteConfigsCache: jest.fn(), }, })); @@ -252,3 +253,13 @@ describe('QonversionInternal - setDeferredPurchasesListener replaces wrapped ent expect(RNQonversion.onDeferredPurchaseCompleted).toHaveBeenCalledTimes(1); }); }); + +describe('invalidateRemoteConfigsCache', () => { + it('passes the call straight through to the native module', () => { + const instance = new QonversionInternal(createConfig()); + + instance.invalidateRemoteConfigsCache(); + + expect(RNQonversion.invalidateRemoteConfigsCache).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/internal/specs/NativeQonversionModule.ts b/src/internal/specs/NativeQonversionModule.ts index d1ee43ff..5bac19ff 100644 --- a/src/internal/specs/NativeQonversionModule.ts +++ b/src/internal/specs/NativeQonversionModule.ts @@ -73,6 +73,7 @@ export interface Spec extends TurboModule { remoteConfig(contextKey: string | undefined): Promise; remoteConfigList(): Promise; remoteConfigListForContextKeys(contextKeys: string[], includeEmptyContextKey: boolean): Promise; + invalidateRemoteConfigsCache(): void; attachUserToExperiment(experimentId: string, groupId: string): Promise; detachUserFromExperiment(experimentId: string): Promise; attachUserToRemoteConfiguration(remoteConfigurationId: string): Promise;