react-native-config 1.6.0 → 1.6.2

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/README.md CHANGED
@@ -26,6 +26,9 @@ Keep in mind this module doesn't obfuscate or encrypt secrets for packaging, so
26
26
 
27
27
  ## Setup
28
28
 
29
+ > ⚠️ Note (Android): react-native-config v1.6.0+ requires React Native 0.74 or higher.
30
+ > If you use an older RN version, see Troubleshooting below.
31
+
29
32
  Install the package:
30
33
 
31
34
  ```
@@ -34,20 +37,34 @@ $ yarn add react-native-config
34
37
 
35
38
  Link the library:
36
39
 
37
- (Note: For React Native 0.60 or greater, [autolinking](https://reactnative.dev/blog/2019/07/03/version-60#native-modules-are-now-autolinked) is available)
40
+ On React Native 0.60 and above there is no link step — the library is
41
+ [autolinked](https://reactnative.dev/blog/2019/07/03/version-60#native-modules-are-now-autolinked).
42
+ Rebuild the app so the native side is picked up. On iOS / macOS, install the pod first:
43
+
44
+ ```
45
+ (cd ios; pod install)
46
+ ```
47
+
48
+ > [!WARNING]
49
+ > Do not link this library manually on React Native 0.60 or above, and do not disable its
50
+ > autolinking in `react-native.config.js`. Autolinking is what registers the native module and,
51
+ > on the New Architecture, generates its TurboModule bindings — adding
52
+ > `include ':react-native-config'` to `android/settings.gradle` instead does neither, and the
53
+ > module then resolves to `null` at runtime. See
54
+ > [TypeError: Cannot read property 'getConfig' of null](#typeerror-cannot-read-property-getconfig-of-null).
38
55
 
39
56
  (Note: For Windows, this module supports autolinking when used with `react-native-windows@0.63`
40
57
  or later. For earlier versions you need to manually link the module.)
41
58
 
59
+ <details>
60
+ <summary><b>Manual linking</b> — only for React Native below 0.60, or react-native-windows below 0.63</summary>
61
+
42
62
  ```
43
63
  $ react-native link react-native-config
44
64
  ```
45
65
 
46
- if cocoapods are used in the project then pod has to be installed as well:
47
-
48
- ```
49
- (cd ios; pod install)
50
- ```
66
+ (`react-native link` was removed from the React Native CLI; it is only available on the older
67
+ versions these instructions apply to.)
51
68
 
52
69
  - Manual Link (iOS / macOS)
53
70
 
@@ -115,6 +132,8 @@ if cocoapods are used in the project then pod has to be installed as well:
115
132
 
116
133
  Add `PackageProviders().Append(winrt::RNCConfig::ReactPackageProvider());` before `InitializeComponent();`.
117
134
 
135
+ </details>
136
+
118
137
  ### Extra step for Android
119
138
  #### Using RN-Integrate
120
139
  Apply extra steps automatically:
@@ -132,16 +151,29 @@ apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.grad
132
151
 
133
152
  #### Advanced Android Setup
134
153
 
135
- In `android/app/build.gradle`, if you use `applicationIdSuffix` or `applicationId` that is different from the package name indicated in `AndroidManifest.xml` in `<manifest package="...">` tag, for example, to support different build variants:
136
- Add this in `android/app/build.gradle`
154
+ `BuildConfig` is generated in your module's `namespace`, which is not always the same as its
155
+ `applicationId` `applicationIdSuffix` and per-flavor `applicationId` change the latter and
156
+ leave the former alone. The library resolves this for you: it looks for `BuildConfig` in the
157
+ package declaring your `Application` class (that is, the `namespace`) before falling back to the
158
+ `applicationId`, so the common variant setups need no extra configuration.
159
+
160
+ If your `BuildConfig` lives somewhere neither of those points at, name the package explicitly in
161
+ `android/app/build.gradle`:
137
162
 
138
163
  ```
139
164
  defaultConfig {
140
165
  ...
141
- resValue "string", "build_config_package", "YOUR_PACKAGE_NAME_IN_ANDROIDMANIFEST_XML"
166
+ resValue "string", "build_config_package", "YOUR_NAMESPACE"
142
167
  }
143
168
  ```
144
169
 
170
+ where `YOUR_NAMESPACE` matches the `namespace` in `android/app/build.gradle` (on React Native
171
+ 0.72 and older, the `package` attribute of `<manifest>` in `AndroidManifest.xml`). This value
172
+ takes priority over the automatic resolution above.
173
+
174
+ If the config arrives in JS as `{}`, check logcat for `ReactConfig: Could not find BuildConfig
175
+ class` — the message lists every package that was tried.
176
+
145
177
  ## TypeScript declaration for your .env file
146
178
 
147
179
  If you want to get autocompletion and typesafety for your .env files. Create a file named `react-native-config.d.ts` in the same directory where you put your type declarations, and add the following contents:
@@ -395,12 +427,56 @@ If using Dexguard, the shrinking phase will remove resources it thinks are unuse
395
427
 
396
428
  -keepresources string/build_config_package
397
429
 
430
+ ### TypeError: Cannot read property 'getConfig' of null
431
+
432
+ The JavaScript side loaded but the native module is not registered in the build, so
433
+ `TurboModuleRegistry` returned `null`. In rough order of likelihood:
434
+
435
+ 1. **The app was not rebuilt** after the library was installed. Restarting Metro does not rebuild
436
+ native code — rebuild the app itself.
437
+ 2. **Autolinking is disabled for this library.** Look for an entry like this in
438
+ `react-native.config.js` and remove it:
439
+
440
+ ```js
441
+ dependencies: {
442
+ 'react-native-config': {
443
+ platforms: { android: null }, // <- remove
444
+ },
445
+ },
446
+ ```
447
+
448
+ 3. **The library is linked manually.** On React Native 0.60+ autolinking replaces manual linking,
449
+ and on the New Architecture a manually linked module is never registered as a TurboModule.
450
+ Remove `include ':react-native-config'` (and the accompanying `project(...)` line) from
451
+ `android/settings.gradle`, and `implementation project(':react-native-config')` from
452
+ `android/app/build.gradle`.
453
+ 4. **iOS only:** `pod install` has not been run since the library was installed.
454
+
455
+ After changing any of the above, rebuild from clean — on Android, delete `android/build` and
456
+ `android/app/build` first, since a stale build can keep the old registration.
457
+
398
458
  ### TypeError: _reactNativeConfig.default.getConstants is not a function
399
459
 
400
460
  This error stems from `.env` file being malformed. Accepted formats are listed here https://regex101.com/r/cbm5Tp/1. Common causes are:
401
461
  - Missing the .env file entirely
402
462
  - Rogue space anywhere, example: in front of env variable: ` MY_ENV='foo'`
403
463
 
464
+ ### Android build error: cannot find symbol BaseReactPackage
465
+
466
+ Starting from **react-native-config v1.6.0**, the Android implementation uses
467
+ `BaseReactPackage` instead of `ReactPackage`.
468
+
469
+ `BaseReactPackage` was introduced in **React Native 0.74**, so projects on React
470
+ Native 0.73 or older will see build errors like:
471
+
472
+ > cannot find symbol
473
+ > class BaseReactPackage
474
+
475
+ To fix this:
476
+
477
+ - Use `react-native-config` **below 1.6.0** (e.g. `1.5.10`), or
478
+ - Upgrade React Native to **0.74 or higher**
479
+
404
480
  ## Testing
405
481
 
406
482
  Since `react-native-config` contains native code, it cannot be run in a node.js environment (Jest, Mocha). [react-native-config-node](https://github.com/CureApp/react-native-config-node) provides a way to mock `react-native-config` for use in test runners - exactly as it is used in the actual app.
@@ -7,12 +7,18 @@ import com.facebook.react.bridge.WritableMap;
7
7
  import java.lang.ClassNotFoundException;
8
8
  import java.lang.IllegalAccessException;
9
9
  import java.lang.reflect.Field;
10
+ import java.util.ArrayList;
11
+ import java.util.LinkedHashSet;
12
+ import java.util.List;
10
13
  import java.util.Map;
11
14
  import java.util.HashMap;
15
+ import java.util.Set;
12
16
 
13
17
  public class RNCConfigModuleImpl {
14
18
  public static final String NAME = "RNCConfigModule";
15
19
 
20
+ private static final String TAG = "ReactNative";
21
+
16
22
  private ReactApplicationContext context;
17
23
 
18
24
  public RNCConfigModuleImpl(ReactApplicationContext context) {
@@ -32,31 +38,78 @@ public class RNCConfigModuleImpl {
32
38
  // because of type safety on the new arch
33
39
  final Map<String, Object> realConstants = new HashMap<>();
34
40
 
35
- try {
36
- int resId = this.context.getResources().getIdentifier("build_config_package", "string", context.getPackageName());
37
- String className;
41
+ final List<String> candidates = buildConfigPackageCandidates();
42
+ Class<?> clazz = null;
43
+
44
+ for (String candidate : candidates) {
38
45
  try {
39
- className = this.context.getString(resId);
40
- } catch (Resources.NotFoundException e) {
41
- className = this.context.getApplicationContext().getPackageName();
46
+ clazz = Class.forName(candidate + ".BuildConfig");
47
+ break;
48
+ }
49
+ catch (ClassNotFoundException e) {
50
+ // Not here - try the next candidate.
42
51
  }
43
- Class clazz = Class.forName(className + ".BuildConfig");
52
+ }
53
+
54
+ if (clazz == null) {
55
+ Log.w(TAG, "ReactConfig: Could not find BuildConfig class. Tried: " + candidates + ". "
56
+ + "If BuildConfig is generated somewhere else, point the library at it with "
57
+ + "`resValue \"string\", \"build_config_package\", \"<your namespace>\"` in android/app/build.gradle.");
58
+ }
59
+ else {
44
60
  Field[] fields = clazz.getDeclaredFields();
45
61
  for(Field f: fields) {
46
62
  try {
47
63
  realConstants.put(f.getName(), f.get(null));
48
64
  }
49
65
  catch (IllegalAccessException e) {
50
- Log.d("ReactNative", "ReactConfig: Could not access BuildConfig field " + f.getName());
66
+ Log.d(TAG, "ReactConfig: Could not access BuildConfig field " + f.getName());
51
67
  }
52
68
  }
53
69
  }
54
- catch (ClassNotFoundException e) {
55
- Log.d("ReactNative", "ReactConfig: Could not find BuildConfig class");
56
- }
57
70
 
58
71
  ret.put("config", realConstants);
59
72
 
60
73
  return MapConverter.convertMapToWritableMap(ret);
61
74
  }
75
+
76
+ /**
77
+ * Packages that may hold the app's generated BuildConfig, most-specific first.
78
+ *
79
+ * BuildConfig is generated in the module's `namespace`, which is not necessarily the
80
+ * applicationId: `applicationIdSuffix` and per-flavor `applicationId` change the latter and
81
+ * leave the former alone. Resolving only via getPackageName() therefore misses BuildConfig on
82
+ * any such variant, and the config silently arrives in JS as {}.
83
+ */
84
+ private List<String> buildConfigPackageCandidates() {
85
+ // LinkedHashSet keeps the priority order below while dropping duplicates, which is the
86
+ // common case: for most apps all three candidates are the same string.
87
+ final Set<String> candidates = new LinkedHashSet<>();
88
+
89
+ // 1. An explicit `resValue "string", "build_config_package", "..."`. Honoured first so that
90
+ // existing setups relying on it keep working, and so it stays an escape hatch when the
91
+ // derived candidates below are wrong.
92
+ int resId = this.context.getResources().getIdentifier(
93
+ "build_config_package", "string", this.context.getPackageName());
94
+ if (resId != 0) {
95
+ try {
96
+ candidates.add(this.context.getString(resId));
97
+ }
98
+ catch (Resources.NotFoundException e) {
99
+ // Declared but unreadable - fall through to the derived candidates.
100
+ }
101
+ }
102
+
103
+ // 2. The package declaring the Application class. That class is part of the app module, so
104
+ // its package is the `namespace` - unaffected by applicationId/applicationIdSuffix.
105
+ Package applicationPackage = this.context.getApplicationContext().getClass().getPackage();
106
+ if (applicationPackage != null) {
107
+ candidates.add(applicationPackage.getName());
108
+ }
109
+
110
+ // 3. The applicationId. Correct whenever it matches the namespace, which is the default.
111
+ candidates.add(this.context.getApplicationContext().getPackageName());
112
+
113
+ return new ArrayList<>(candidates);
114
+ }
62
115
  }
@@ -35,7 +35,7 @@ public class RNCConfigPackage extends BaseReactPackage {
35
35
  RNCConfigModule.NAME,
36
36
  RNCConfigModule.NAME,
37
37
  false, // canOverrideExistingModule
38
- false, // needsEagerInit
38
+ true, // needsEagerInit
39
39
  false, // hasConstants
40
40
  false, // isCxxModule
41
41
  isTurboModule // isTurboModule
package/index.js CHANGED
@@ -1,5 +1,27 @@
1
1
  "use strict";
2
2
 
3
- export const Config =
4
- require("./codegen/NativeConfigModule").default.getConfig().config;
3
+ const NativeConfigModule = require("./codegen/NativeConfigModule").default;
4
+
5
+ if (NativeConfigModule == null) {
6
+ // TurboModuleRegistry.get returns null when the native module is not registered in the app.
7
+ // Left alone, the next line fails with "Cannot read property 'getConfig' of null", which says
8
+ // nothing about the cause - so spell out the ones that actually produce this.
9
+ throw new Error(
10
+ "react-native-config: the native module RNCConfigModule was not found.\n\n" +
11
+ "JavaScript loaded, but the native side is not registered in this build. Common causes:\n\n" +
12
+ " 1. The app was not rebuilt after installing the library. Restarting Metro is not enough -\n" +
13
+ " rebuild the native app.\n" +
14
+ " 2. Autolinking is disabled for this library. Check react-native.config.js for a\n" +
15
+ " `dependencies` entry setting `platforms.android` or `platforms.ios` to null, and\n" +
16
+ " remove it.\n" +
17
+ " 3. The library is linked manually. React Native 0.60+ autolinks it, and on the New\n" +
18
+ " Architecture a manually linked module is not registered as a TurboModule. Remove\n" +
19
+ " `include ':react-native-config'` from android/settings.gradle and the matching\n" +
20
+ " `implementation project(':react-native-config')` from android/app/build.gradle.\n" +
21
+ " 4. iOS only: `pod install` has not run since the library was installed.\n\n" +
22
+ "See https://github.com/react-native-config/react-native-config#troubleshooting"
23
+ );
24
+ }
25
+
26
+ export const Config = NativeConfigModule.getConfig().config;
5
27
  export default Config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-config",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Expose config variables to React Native apps",
5
5
  "keywords": [
6
6
  "env",
@@ -12,14 +12,14 @@
12
12
  "windows",
13
13
  "12factor"
14
14
  ],
15
- "homepage": "https://github.com/luggit/react-native-config",
15
+ "homepage": "https://github.com/react-native-config/react-native-config",
16
16
  "contributors": [
17
17
  "Luan Curti <luancurti@gmail.com> (https://github.com/luancurti)",
18
18
  "Amer Lotfi Orimi <amerllica@gmail.com> (https://github.com/amerllica)"
19
19
  ],
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "https://github.com/luggit/react-native-config"
22
+ "url": "git+https://github.com/react-native-config/react-native-config.git"
23
23
  },
24
24
  "private": false,
25
25
  "author": "Pedro Belo",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "ios": {
64
64
  "modules": {
65
- "RNCConfig": {
65
+ "RNCConfigModule": {
66
66
  "className": "RNCConfigModule",
67
67
  "unstableRequiresMainQueueSetup": true
68
68
  }