@react-native-firebase/remote-config 18.0.0 → 18.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/android/src/reactnative/java/io/invertase/firebase/config/ReactNativeFirebaseConfigModule.java +105 -1
- package/ios/RNFBConfig/RNFBConfigModule.m +82 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +56 -1
- package/lib/modular/index.js +11 -0
- package/lib/version.js +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,22 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
## [18.2.0](https://github.com/invertase/react-native-firebase/compare/v18.1.0...v18.2.0) (2023-07-13)
|
7
|
+
|
8
|
+
### Bug Fixes
|
9
|
+
|
10
|
+
- **app, ios:** incorporate firebase-ios-sdk 10.12.0 ([#7231](https://github.com/invertase/react-native-firebase/issues/7231)) ([ee66459](https://github.com/invertase/react-native-firebase/commit/ee66459cd214ffb84ce2d4e15eef79d047f075ab))
|
11
|
+
|
12
|
+
## [18.1.0](https://github.com/invertase/react-native-firebase/compare/v18.0.0...v18.1.0) (2023-06-22)
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
- **remote-config:** realtime config updates ([9ded619](https://github.com/invertase/react-native-firebase/commit/9ded619e81c1523d7fa0cdbda8fc94929450a967))
|
17
|
+
|
18
|
+
### Bug Fixes
|
19
|
+
|
20
|
+
- **remote-config, ios:** workaround firebase-ios-sdk[#11458](https://github.com/invertase/react-native-firebase/issues/11458) until SDK v10.12.0 ([8c75849](https://github.com/invertase/react-native-firebase/commit/8c758496c2fadc506805f81d1619ebce21a413df))
|
21
|
+
|
6
22
|
## [18.0.0](https://github.com/invertase/react-native-firebase/compare/v17.5.0...v18.0.0) (2023-06-05)
|
7
23
|
|
8
24
|
**Note:** Version bump only for package @react-native-firebase/remote-config
|
@@ -19,21 +19,46 @@ package io.invertase.firebase.config;
|
|
19
19
|
|
20
20
|
import com.facebook.react.bridge.*;
|
21
21
|
import com.google.firebase.FirebaseApp;
|
22
|
-
import com.google.firebase.remoteconfig
|
22
|
+
import com.google.firebase.remoteconfig.*;
|
23
|
+
import io.invertase.firebase.common.ReactNativeFirebaseEvent;
|
24
|
+
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
|
23
25
|
import io.invertase.firebase.common.ReactNativeFirebaseModule;
|
26
|
+
import java.util.ArrayList;
|
24
27
|
import java.util.HashMap;
|
28
|
+
import java.util.Iterator;
|
29
|
+
import java.util.List;
|
25
30
|
import java.util.Map;
|
31
|
+
import java.util.Set;
|
26
32
|
import javax.annotation.Nullable;
|
33
|
+
import org.jetbrains.annotations.NotNull;
|
27
34
|
|
28
35
|
public class ReactNativeFirebaseConfigModule extends ReactNativeFirebaseModule {
|
29
36
|
private static final String SERVICE_NAME = "Config";
|
30
37
|
private final UniversalFirebaseConfigModule module;
|
31
38
|
|
39
|
+
private static HashMap<String, ConfigUpdateListenerRegistration> mConfigUpdateRegistrations =
|
40
|
+
new HashMap<>();
|
41
|
+
|
32
42
|
ReactNativeFirebaseConfigModule(ReactApplicationContext reactContext) {
|
33
43
|
super(reactContext, SERVICE_NAME);
|
34
44
|
module = new UniversalFirebaseConfigModule(reactContext, SERVICE_NAME);
|
35
45
|
}
|
36
46
|
|
47
|
+
@Override
|
48
|
+
public void onCatalystInstanceDestroy() {
|
49
|
+
super.onCatalystInstanceDestroy();
|
50
|
+
|
51
|
+
Iterator<Map.Entry<String, ConfigUpdateListenerRegistration>> configRegistrationsIterator =
|
52
|
+
mConfigUpdateRegistrations.entrySet().iterator();
|
53
|
+
|
54
|
+
while (configRegistrationsIterator.hasNext()) {
|
55
|
+
Map.Entry<String, ConfigUpdateListenerRegistration> pair = configRegistrationsIterator.next();
|
56
|
+
ConfigUpdateListenerRegistration mConfigRegistration = pair.getValue();
|
57
|
+
mConfigRegistration.remove();
|
58
|
+
configRegistrationsIterator.remove();
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
37
62
|
@ReactMethod
|
38
63
|
public void activate(String appName, Promise promise) {
|
39
64
|
module
|
@@ -151,6 +176,85 @@ public class ReactNativeFirebaseConfigModule extends ReactNativeFirebaseModule {
|
|
151
176
|
});
|
152
177
|
}
|
153
178
|
|
179
|
+
@ReactMethod
|
180
|
+
public void onConfigUpdated(String appName) {
|
181
|
+
if (mConfigUpdateRegistrations.get(appName) == null) {
|
182
|
+
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
183
|
+
ConfigUpdateListenerRegistration registration =
|
184
|
+
FirebaseRemoteConfig.getInstance(firebaseApp)
|
185
|
+
.addOnConfigUpdateListener(
|
186
|
+
new ConfigUpdateListener() {
|
187
|
+
@Override
|
188
|
+
public void onUpdate(@NotNull ConfigUpdate configUpdate) {
|
189
|
+
ReactNativeFirebaseEventEmitter emitter =
|
190
|
+
ReactNativeFirebaseEventEmitter.getSharedInstance();
|
191
|
+
|
192
|
+
Set<String> updatedKeys = configUpdate.getUpdatedKeys();
|
193
|
+
List<String> updatedKeysList = new ArrayList<>(updatedKeys);
|
194
|
+
|
195
|
+
Map<String, Object> results = new HashMap<>();
|
196
|
+
results.put("appName", appName);
|
197
|
+
results.put("resultType", "success");
|
198
|
+
results.put("updatedKeys", updatedKeysList);
|
199
|
+
ReactNativeFirebaseEvent event =
|
200
|
+
new ReactNativeFirebaseEvent(
|
201
|
+
"on_config_updated", Arguments.makeNativeMap(results), appName);
|
202
|
+
emitter.sendEvent(event);
|
203
|
+
}
|
204
|
+
|
205
|
+
@Override
|
206
|
+
public void onError(@NotNull FirebaseRemoteConfigException error) {
|
207
|
+
ReactNativeFirebaseEventEmitter emitter =
|
208
|
+
ReactNativeFirebaseEventEmitter.getSharedInstance();
|
209
|
+
|
210
|
+
WritableMap userInfoMap = Arguments.createMap();
|
211
|
+
userInfoMap.putString("resultType", "error");
|
212
|
+
userInfoMap.putString("appName", appName);
|
213
|
+
|
214
|
+
FirebaseRemoteConfigException.Code code = error.getCode();
|
215
|
+
switch (code) {
|
216
|
+
case CONFIG_UPDATE_STREAM_ERROR:
|
217
|
+
userInfoMap.putString("code", "config_update_stream_error");
|
218
|
+
break;
|
219
|
+
case CONFIG_UPDATE_MESSAGE_INVALID:
|
220
|
+
userInfoMap.putString("code", "config_update_message_invalid");
|
221
|
+
break;
|
222
|
+
case CONFIG_UPDATE_NOT_FETCHED:
|
223
|
+
userInfoMap.putString("code", "config_update_not_fetched");
|
224
|
+
break;
|
225
|
+
case CONFIG_UPDATE_UNAVAILABLE:
|
226
|
+
userInfoMap.putString("code", "config_update_unavailable");
|
227
|
+
break;
|
228
|
+
case UNKNOWN:
|
229
|
+
userInfoMap.putString("code", "unknown");
|
230
|
+
break;
|
231
|
+
default:
|
232
|
+
// contract violation internal to the RNFB / SDK boundary
|
233
|
+
userInfoMap.putString("code", "internal");
|
234
|
+
}
|
235
|
+
|
236
|
+
userInfoMap.putString("message", error.getMessage());
|
237
|
+
userInfoMap.putString("nativeErrorMessage", error.getMessage());
|
238
|
+
ReactNativeFirebaseEvent event =
|
239
|
+
new ReactNativeFirebaseEvent("on_config_updated", userInfoMap, appName);
|
240
|
+
emitter.sendEvent(event);
|
241
|
+
}
|
242
|
+
});
|
243
|
+
|
244
|
+
mConfigUpdateRegistrations.put(appName, registration);
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
@ReactMethod
|
249
|
+
public void removeConfigUpdateRegistration(String appName) {
|
250
|
+
ConfigUpdateListenerRegistration mConfigRegistration = mConfigUpdateRegistrations.get(appName);
|
251
|
+
|
252
|
+
if (mConfigRegistration != null) {
|
253
|
+
mConfigRegistration.remove();
|
254
|
+
mConfigUpdateRegistrations.remove(appName);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
154
258
|
private WritableMap resultWithConstants(Object result) {
|
155
259
|
Map<String, Object> responseMap = new HashMap<>(2);
|
156
260
|
responseMap.put("result", result);
|
@@ -22,6 +22,10 @@
|
|
22
22
|
#import "RNFBConfigModule.h"
|
23
23
|
#import "RNFBSharedUtils.h"
|
24
24
|
|
25
|
+
static NSString *const ON_CONFIG_UPDATED_EVENT = @"on_config_updated";
|
26
|
+
|
27
|
+
static __strong NSMutableDictionary *configUpdateHandlers;
|
28
|
+
|
25
29
|
@implementation RNFBConfigModule
|
26
30
|
#pragma mark -
|
27
31
|
#pragma mark Converters
|
@@ -64,6 +68,21 @@ NSString *convertFIRRemoteConfigSourceToNSString(FIRRemoteConfigSource value) {
|
|
64
68
|
}
|
65
69
|
}
|
66
70
|
|
71
|
+
NSString *convertFIRRemoteConfigUpdateErrorToNSString(FIRRemoteConfigUpdateError value) {
|
72
|
+
switch (value) {
|
73
|
+
case FIRRemoteConfigUpdateErrorStreamError:
|
74
|
+
return @"config_update_stream_error";
|
75
|
+
case FIRRemoteConfigUpdateErrorMessageInvalid:
|
76
|
+
return @"config_update_message_invalid";
|
77
|
+
case FIRRemoteConfigUpdateErrorNotFetched:
|
78
|
+
return @"config_update_not_fetched";
|
79
|
+
case FIRRemoteConfigUpdateErrorUnavailable:
|
80
|
+
return @"config_update_unavailable";
|
81
|
+
default:
|
82
|
+
return @"internal";
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
67
86
|
NSDictionary *convertFIRRemoteConfigValueToNSDictionary(FIRRemoteConfigValue *value) {
|
68
87
|
return @{
|
69
88
|
@"value" : (id)value.stringValue ?: [NSNull null],
|
@@ -84,6 +103,28 @@ RCT_EXPORT_MODULE();
|
|
84
103
|
return YES;
|
85
104
|
}
|
86
105
|
|
106
|
+
- (id)init {
|
107
|
+
self = [super init];
|
108
|
+
static dispatch_once_t onceToken;
|
109
|
+
dispatch_once(&onceToken, ^{
|
110
|
+
configUpdateHandlers = [[NSMutableDictionary alloc] init];
|
111
|
+
});
|
112
|
+
return self;
|
113
|
+
}
|
114
|
+
|
115
|
+
- (void)dealloc {
|
116
|
+
[self invalidate];
|
117
|
+
}
|
118
|
+
|
119
|
+
- (void)invalidate {
|
120
|
+
for (NSString *key in configUpdateHandlers) {
|
121
|
+
FIRConfigUpdateListenerRegistration *registration = [configUpdateHandlers objectForKey:key];
|
122
|
+
[registration remove];
|
123
|
+
}
|
124
|
+
|
125
|
+
[configUpdateHandlers removeAllObjects];
|
126
|
+
}
|
127
|
+
|
87
128
|
#pragma mark -
|
88
129
|
#pragma mark Firebase Config Methods
|
89
130
|
|
@@ -223,6 +264,47 @@ RCT_EXPORT_METHOD(setDefaultsFromResource
|
|
223
264
|
}
|
224
265
|
}
|
225
266
|
|
267
|
+
RCT_EXPORT_METHOD(onConfigUpdated : (FIRApp *)firebaseApp) {
|
268
|
+
if (![configUpdateHandlers valueForKey:firebaseApp.name]) {
|
269
|
+
FIRConfigUpdateListenerRegistration *newRegistration =
|
270
|
+
[[FIRRemoteConfig remoteConfigWithApp:firebaseApp]
|
271
|
+
addOnConfigUpdateListener:^(FIRRemoteConfigUpdate *_Nonnull configUpdate,
|
272
|
+
NSError *_Nullable error) {
|
273
|
+
if (error != nil) {
|
274
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
275
|
+
|
276
|
+
[userInfo setValue:@"error" forKey:@"resultType"];
|
277
|
+
[userInfo setValue:convertFIRRemoteConfigUpdateErrorToNSString(error.code)
|
278
|
+
forKey:@"code"];
|
279
|
+
[userInfo setValue:error.localizedDescription forKey:@"message"];
|
280
|
+
[userInfo setValue:error.localizedDescription forKey:@"nativeErrorMessage"];
|
281
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
282
|
+
name:ON_CONFIG_UPDATED_EVENT
|
283
|
+
body:userInfo];
|
284
|
+
return;
|
285
|
+
}
|
286
|
+
|
287
|
+
NSMutableDictionary *results = [NSMutableDictionary dictionary];
|
288
|
+
|
289
|
+
[results setValue:@"success" forKey:@"resultType"];
|
290
|
+
[results setValue:[configUpdate.updatedKeys allObjects] forKey:@"updatedKeys"];
|
291
|
+
|
292
|
+
[RNFBSharedUtils sendJSEventForApp:firebaseApp
|
293
|
+
name:ON_CONFIG_UPDATED_EVENT
|
294
|
+
body:results];
|
295
|
+
}];
|
296
|
+
|
297
|
+
configUpdateHandlers[firebaseApp.name] = newRegistration;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
RCT_EXPORT_METHOD(removeConfigUpdateRegistration : (FIRApp *)firebaseApp) {
|
302
|
+
if ([configUpdateHandlers valueForKey:firebaseApp.name]) {
|
303
|
+
[[configUpdateHandlers objectForKey:firebaseApp.name] remove];
|
304
|
+
[configUpdateHandlers removeObjectForKey:firebaseApp.name];
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
226
308
|
#pragma mark -
|
227
309
|
#pragma mark Internal Helper Methods
|
228
310
|
|
package/lib/index.d.ts
CHANGED
@@ -376,6 +376,17 @@ export namespace FirebaseRemoteConfigTypes {
|
|
376
376
|
*/
|
377
377
|
setDefaultsFromResource(resourceName: string): Promise<null>;
|
378
378
|
|
379
|
+
/**
|
380
|
+
* Start listening for real-time config updates from the Remote Config backend and
|
381
|
+
* automatically fetch updates when they’re available. Note that the list of updated keys
|
382
|
+
* passed to the callback will include all keys not currently active, and the config update
|
383
|
+
* process fetches the new config but does not automatically activate for you. Typically
|
384
|
+
* you will want to activate the config in your callback so the new values are in force.
|
385
|
+
*
|
386
|
+
* @param listener called with either array of updated keys or error arg when config changes
|
387
|
+
*/
|
388
|
+
onConfigUpdated(listener: CallbackOrObserver<OnConfigUpdatedListenerCallback>): () => void;
|
389
|
+
|
379
390
|
/**
|
380
391
|
* Moves fetched data to the apps active config.
|
381
392
|
* Resolves with a boolean value true if new local values were activated
|
@@ -543,6 +554,17 @@ export const firebase: ReactNativeFirebase.Module & {
|
|
543
554
|
): ReactNativeFirebase.FirebaseApp & { remoteConfig(): FirebaseRemoteConfigTypes.Module };
|
544
555
|
};
|
545
556
|
|
557
|
+
type CallbackOrObserver<T extends (...args: any[]) => any> = T | { next: T };
|
558
|
+
|
559
|
+
type OnConfigUpdatedListenerCallback = (
|
560
|
+
event?: { updatedKeys: string[] },
|
561
|
+
error?: {
|
562
|
+
code: string;
|
563
|
+
message: string;
|
564
|
+
nativeErrorMessage: string;
|
565
|
+
},
|
566
|
+
) => void;
|
567
|
+
|
546
568
|
export default defaultExport;
|
547
569
|
|
548
570
|
/**
|
package/lib/index.js
CHANGED
@@ -53,6 +53,7 @@ export {
|
|
53
53
|
fetch,
|
54
54
|
setDefaults,
|
55
55
|
setDefaultsFromResource,
|
56
|
+
onConfigUpdated,
|
56
57
|
} from './modular/index';
|
57
58
|
|
58
59
|
const statics = {
|
@@ -84,6 +85,7 @@ class FirebaseConfigModule extends FirebaseModule {
|
|
84
85
|
this._lastFetchTime = -1;
|
85
86
|
this._values = {};
|
86
87
|
this._isWeb = Platform.OS !== 'ios' && Platform.OS !== 'android';
|
88
|
+
this._configUpdateListenerCount = 0;
|
87
89
|
}
|
88
90
|
|
89
91
|
get defaultConfig() {
|
@@ -282,6 +284,59 @@ class FirebaseConfigModule extends FirebaseModule {
|
|
282
284
|
return this._promiseWithConstants(this.native.setDefaultsFromResource(resourceName));
|
283
285
|
}
|
284
286
|
|
287
|
+
/**
|
288
|
+
* Registers a listener to changes in the configuration.
|
289
|
+
*
|
290
|
+
* @param listenerOrObserver - function called on config change
|
291
|
+
* @returns {function} unsubscribe listener
|
292
|
+
*/
|
293
|
+
onConfigUpdated(listenerOrObserver) {
|
294
|
+
const listener = this._parseListener(listenerOrObserver);
|
295
|
+
let unsubscribed = false;
|
296
|
+
const subscription = this.emitter.addListener(
|
297
|
+
this.eventNameForApp('on_config_updated'),
|
298
|
+
event => {
|
299
|
+
const { resultType } = event;
|
300
|
+
if (resultType === 'success') {
|
301
|
+
listener({ updatedKeys: event.updatedKeys }, undefined);
|
302
|
+
return;
|
303
|
+
}
|
304
|
+
|
305
|
+
listener(undefined, {
|
306
|
+
code: event.code,
|
307
|
+
message: event.message,
|
308
|
+
nativeErrorMessage: event.nativeErrorMessage,
|
309
|
+
});
|
310
|
+
},
|
311
|
+
);
|
312
|
+
if (this._configUpdateListenerCount === 0) {
|
313
|
+
this.native.onConfigUpdated();
|
314
|
+
}
|
315
|
+
|
316
|
+
this._configUpdateListenerCount++;
|
317
|
+
|
318
|
+
return () => {
|
319
|
+
if (unsubscribed) {
|
320
|
+
// there is no harm in calling this multiple times to unsubscribe,
|
321
|
+
// but anything after the first call is a no-op
|
322
|
+
return;
|
323
|
+
} else {
|
324
|
+
unsubscribed = true;
|
325
|
+
}
|
326
|
+
subscription.remove();
|
327
|
+
this._configUpdateListenerCount--;
|
328
|
+
if (this._configUpdateListenerCount === 0) {
|
329
|
+
this.native.removeConfigUpdateRegistration();
|
330
|
+
}
|
331
|
+
};
|
332
|
+
}
|
333
|
+
|
334
|
+
_parseListener(listenerOrObserver) {
|
335
|
+
return typeof listenerOrObserver === 'object'
|
336
|
+
? listenerOrObserver.next.bind(listenerOrObserver)
|
337
|
+
: listenerOrObserver;
|
338
|
+
}
|
339
|
+
|
285
340
|
_updateFromConstants(constants) {
|
286
341
|
// Wrapped this as we update using sync getters initially for `defaultConfig` & `settings`
|
287
342
|
if (constants.lastFetchTime) {
|
@@ -326,7 +381,7 @@ export default createModuleNamespace({
|
|
326
381
|
version,
|
327
382
|
namespace,
|
328
383
|
nativeModuleName,
|
329
|
-
nativeEvents:
|
384
|
+
nativeEvents: ['on_config_updated'],
|
330
385
|
hasMultiAppSupport: true,
|
331
386
|
hasCustomUrlOrRegionSupport: false,
|
332
387
|
ModuleClass: FirebaseConfigModule,
|
package/lib/modular/index.js
CHANGED
@@ -221,3 +221,14 @@ export function setDefaults(remoteConfig, defaults) {
|
|
221
221
|
export function setDefaultsFromResource(remoteConfig, resourceName) {
|
222
222
|
return remoteConfig.setDefaultsFromResource(resourceName);
|
223
223
|
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
* Registers a listener to changes in the configuration.
|
227
|
+
*
|
228
|
+
* @param remoteConfig - RemoteConfig instance
|
229
|
+
* @param callback - function called on config change
|
230
|
+
* @returns {function} unsubscribe listener
|
231
|
+
*/
|
232
|
+
export function onConfigUpdated(remoteConfig, callback) {
|
233
|
+
return remoteConfig.onConfigUpdated(callback);
|
234
|
+
}
|
package/lib/version.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
// Generated by genversion.
|
2
|
-
module.exports = '18.
|
2
|
+
module.exports = '18.2.0';
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@react-native-firebase/remote-config",
|
3
|
-
"version": "18.
|
3
|
+
"version": "18.2.0",
|
4
4
|
"author": "Invertase <oss@invertase.io> (http://invertase.io)",
|
5
5
|
"description": "React Native Firebase - React Native Firebase provides native integration with Remote Config, allowing you to change the appearance and/or functionality of your app without requiring an app update.",
|
6
6
|
"main": "lib/index.js",
|
@@ -24,11 +24,11 @@
|
|
24
24
|
"remote-config"
|
25
25
|
],
|
26
26
|
"peerDependencies": {
|
27
|
-
"@react-native-firebase/analytics": "18.
|
28
|
-
"@react-native-firebase/app": "18.
|
27
|
+
"@react-native-firebase/analytics": "18.2.0",
|
28
|
+
"@react-native-firebase/app": "18.2.0"
|
29
29
|
},
|
30
30
|
"publishConfig": {
|
31
31
|
"access": "public"
|
32
32
|
},
|
33
|
-
"gitHead": "
|
33
|
+
"gitHead": "4c666df92028ddc3c0010a7ac102f54b600e6644"
|
34
34
|
}
|