@bravemobile/react-native-code-push 9.0.0 → 10.0.0-beta.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/CodePush.podspec CHANGED
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
9
9
  s.author = package['author']
10
10
  s.license = package['license']
11
11
  s.homepage = package['homepage']
12
- s.source = { :git => 'https://github.com/microsoft/react-native-code-push.git', :tag => "v#{s.version}"}
12
+ s.source = { :git => 'https://github.com/Soomgo-Mobile/react-native-code-push.git', :tag => "v#{s.version}"}
13
13
  s.ios.deployment_target = '9.0'
14
14
  s.tvos.deployment_target = '9.0'
15
15
  s.preserve_paths = '*.js'
package/README.md CHANGED
@@ -10,9 +10,18 @@ It allows self-hosting of CodePush deployments while retaining essential operati
10
10
 
11
11
 
12
12
 
13
- > [!NOTE]
14
- > New architecture will be supported later.
13
+ ### 🚀 New Architecture support
14
+
15
+ Tested on the React Native template apps
15
16
 
17
+ | RN Version | Old Architecture | New Architecture | New Architecture Bridgeless |
18
+ |--------|--------|--------|--------|
19
+ | 0.73.11 | ✅ | ✅ | Unsupported |
20
+ | 0.74.7 | ✅ | ✅ | ✅ |
21
+ | 0.75.5 | ✅ | ✅ | ✅ |
22
+ | 0.76.7 | ✅ | ✅ | ✅ |
23
+ | 0.77.1 | ✅ | ✅ | ✅ |
24
+ | 0.78.0 | ✅ | ✅ | ✅ |
16
25
 
17
26
 
18
27
  ## 🚗 Migration Guide
@@ -211,7 +220,7 @@ export default CodePush({
211
220
  ### 5. Configure the CLI Tool
212
221
 
213
222
  > [!TIP]
214
- > For a more detailed and practical example, refer to the `CodePushDemoApp` in `example` directory.
223
+ > For a more detailed and practical example, refer to the `CodePushDemoApp` in `example` directory. ([link](https://github.com/Soomgo-Mobile/react-native-code-push/tree/master/Examples/CodePushDemoApp))
215
224
 
216
225
  **(1) Create a `code-push.config.ts` file in the root directory of your project.**
217
226
 
@@ -50,15 +50,18 @@ public class CodePush implements ReactPackage {
50
50
  private static ReactInstanceHolder mReactInstanceHolder;
51
51
  private static CodePush mCurrentInstance;
52
52
 
53
- public CodePush(Context context) {
54
- this(context, false);
55
- }
56
-
57
53
  public static String getServiceUrl() {
58
54
  return mServerUrl;
59
55
  }
60
56
 
61
- public CodePush(Context context, boolean isDebugMode) {
57
+ public static synchronized CodePush getInstance(Context context, boolean isDebugMode) {
58
+ if (mCurrentInstance == null) {
59
+ mCurrentInstance = new CodePush(context, isDebugMode);
60
+ }
61
+ return mCurrentInstance;
62
+ }
63
+
64
+ private CodePush(Context context, boolean isDebugMode) {
62
65
  mContext = context.getApplicationContext();
63
66
 
64
67
  mUpdateManager = new CodePushUpdateManager(context.getFilesDir().getAbsolutePath());
@@ -88,27 +91,6 @@ public class CodePush implements ReactPackage {
88
91
  initializeUpdateAfterRestart();
89
92
  }
90
93
 
91
- public CodePush(Context context, boolean isDebugMode, String serverUrl) {
92
- this(context, isDebugMode);
93
- mServerUrl = serverUrl;
94
- }
95
-
96
- public CodePush(Context context, boolean isDebugMode, int publicKeyResourceDescriptor) {
97
- this(context, isDebugMode);
98
-
99
- mPublicKey = getPublicKeyByResourceDescriptor(publicKeyResourceDescriptor);
100
- }
101
-
102
- public CodePush(Context context, boolean isDebugMode, String serverUrl, Integer publicKeyResourceDescriptor) {
103
- this(context, isDebugMode);
104
-
105
- if (publicKeyResourceDescriptor != null) {
106
- mPublicKey = getPublicKeyByResourceDescriptor(publicKeyResourceDescriptor);
107
- }
108
-
109
- mServerUrl = serverUrl;
110
- }
111
-
112
94
  private String getPublicKeyByResourceDescriptor(int publicKeyResourceDescriptor){
113
95
  String publicKey;
114
96
  try {
@@ -5,11 +5,15 @@ import android.content.SharedPreferences;
5
5
  import android.os.AsyncTask;
6
6
  import android.os.Handler;
7
7
  import android.os.Looper;
8
- import android.provider.Settings;
9
8
  import android.view.View;
10
9
 
10
+ import androidx.annotation.OptIn;
11
+
11
12
  import com.facebook.react.ReactApplication;
13
+ import com.facebook.react.ReactDelegate;
14
+ import com.facebook.react.ReactHost;
12
15
  import com.facebook.react.ReactInstanceManager;
16
+ import com.facebook.react.ReactActivity;
13
17
  import com.facebook.react.ReactRootView;
14
18
  import com.facebook.react.bridge.Arguments;
15
19
  import com.facebook.react.bridge.JSBundleLoader;
@@ -20,9 +24,11 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
20
24
  import com.facebook.react.bridge.ReactMethod;
21
25
  import com.facebook.react.bridge.ReadableMap;
22
26
  import com.facebook.react.bridge.WritableMap;
27
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI;
23
28
  import com.facebook.react.modules.core.ChoreographerCompat;
24
29
  import com.facebook.react.modules.core.DeviceEventManagerModule;
25
30
  import com.facebook.react.modules.core.ReactChoreographer;
31
+ import com.facebook.react.runtime.ReactHostDelegate;
26
32
 
27
33
  import org.json.JSONArray;
28
34
  import org.json.JSONException;
@@ -30,10 +36,10 @@ import org.json.JSONObject;
30
36
 
31
37
  import java.io.IOException;
32
38
  import java.lang.reflect.Field;
39
+ import java.lang.reflect.Method;
33
40
  import java.util.ArrayList;
34
41
  import java.util.Date;
35
42
  import java.util.HashMap;
36
- import java.util.List;
37
43
  import java.util.Map;
38
44
  import java.util.UUID;
39
45
 
@@ -120,15 +126,38 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
120
126
  latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
121
127
  }
122
128
 
123
- Field bundleLoaderField = instanceManager.getClass().getDeclaredField("mBundleLoader");
124
- bundleLoaderField.setAccessible(true);
125
- bundleLoaderField.set(instanceManager, latestJSBundleLoader);
129
+ ReactHost reactHost = resolveReactHost();
130
+ if (reactHost == null) {
131
+ // Bridge, Old Architecture and RN < 0.74 (we support Bridgeless >= 0.74)
132
+ setJSBundleLoaderBridge(instanceManager, latestJSBundleLoader);
133
+ return;
134
+ }
135
+
136
+ // Bridgeless (RN >= 0.74)
137
+ setJSBundleLoaderBridgeless(reactHost, latestJSBundleLoader);
126
138
  } catch (Exception e) {
127
139
  CodePushUtils.log("Unable to set JSBundle - CodePush may not support this version of React Native");
128
140
  throw new IllegalAccessException("Could not setJSBundle");
129
141
  }
130
142
  }
131
143
 
144
+ private void setJSBundleLoaderBridge(ReactInstanceManager instanceManager, JSBundleLoader latestJSBundleLoader) throws NoSuchFieldException, IllegalAccessException {
145
+ Field bundleLoaderField = instanceManager.getClass().getDeclaredField("mBundleLoader");
146
+ bundleLoaderField.setAccessible(true);
147
+ bundleLoaderField.set(instanceManager, latestJSBundleLoader);
148
+ }
149
+
150
+ @OptIn(markerClass = UnstableReactNativeAPI.class)
151
+ private void setJSBundleLoaderBridgeless(ReactHost reactHost, JSBundleLoader latestJSBundleLoader) throws NoSuchFieldException, IllegalAccessException {
152
+ Field mReactHostDelegateField = reactHost.getClass().getDeclaredField("mReactHostDelegate");
153
+ mReactHostDelegateField.setAccessible(true);
154
+ ReactHostDelegate reactHostDelegate = (ReactHostDelegate) mReactHostDelegateField.get(reactHost);
155
+ assert reactHostDelegate != null;
156
+ Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
157
+ jsBundleLoaderField.setAccessible(true);
158
+ jsBundleLoaderField.set(reactHostDelegate, latestJSBundleLoader);
159
+ }
160
+
132
161
  private void loadBundle() {
133
162
  clearLifecycleEventListener();
134
163
  try {
@@ -156,12 +185,22 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
156
185
  @Override
157
186
  public void run() {
158
187
  try {
159
- // We don't need to resetReactRootViews anymore
160
- // due the issue https://github.com/facebook/react-native/issues/14533
161
- // has been fixed in RN 0.46.0
162
- //resetReactRootViews(instanceManager);
188
+ // reload method introduced in RN 0.74 (https://github.com/reactwg/react-native-new-architecture/discussions/174)
189
+ // so, we need to check if reload method exists and call it
190
+ try {
191
+ ReactDelegate reactDelegate = resolveReactDelegate();
192
+ if (reactDelegate == null) {
193
+ throw new NoSuchMethodException("ReactDelegate doesn't have reload method in RN < 0.74");
194
+ }
195
+
196
+ resetReactRootViews(reactDelegate);
163
197
 
164
- instanceManager.recreateReactContextInBackground();
198
+ Method reloadMethod = reactDelegate.getClass().getMethod("reload");
199
+ reloadMethod.invoke(reactDelegate);
200
+ } catch (NoSuchMethodException e) {
201
+ // RN < 0.74 calls ReactInstanceManager.recreateReactContextInBackground() directly
202
+ instanceManager.recreateReactContextInBackground();
203
+ }
165
204
  mCodePush.initializeUpdateAfterRestart();
166
205
  } catch (Exception e) {
167
206
  // The recreation method threw an unknown exception
@@ -179,18 +218,19 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
179
218
  }
180
219
  }
181
220
 
182
- // This workaround has been implemented in order to fix https://github.com/facebook/react-native/issues/14533
183
- // resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
184
- // This fix also relates to https://github.com/microsoft/react-native-code-push/issues/878
185
- private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
186
- Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
187
- mAttachedRootViewsField.setAccessible(true);
188
- List<ReactRootView> mAttachedRootViews = (List<ReactRootView>)mAttachedRootViewsField.get(instanceManager);
189
- for (ReactRootView reactRootView : mAttachedRootViews) {
190
- reactRootView.removeAllViews();
191
- reactRootView.setId(View.NO_ID);
221
+ // Fix freezing that occurs when reloading the app (RN >= 0.77.1 Old Architecture)
222
+ // - "Trying to add a root view with an explicit id (11) already set.
223
+ // React Native uses the id field to track react tags and will overwrite this field.
224
+ // If that is fine, explicitly overwrite the id field to View.NO_ID before calling addRootView."
225
+ private void resetReactRootViews(ReactDelegate reactDelegate) {
226
+ ReactActivity currentActivity = (ReactActivity) getCurrentActivity();
227
+ if (currentActivity != null) {
228
+ ReactRootView reactRootView = reactDelegate.getReactRootView();
229
+ if (reactRootView != null) {
230
+ reactRootView.removeAllViews();
231
+ reactRootView.setId(View.NO_ID);
232
+ }
192
233
  }
193
- mAttachedRootViewsField.set(instanceManager, mAttachedRootViews);
194
234
  }
195
235
 
196
236
  private void clearLifecycleEventListener() {
@@ -201,6 +241,36 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
201
241
  }
202
242
  }
203
243
 
244
+ private ReactDelegate resolveReactDelegate() {
245
+ ReactActivity currentActivity = (ReactActivity) getCurrentActivity();
246
+ if (currentActivity == null) {
247
+ return null;
248
+ }
249
+
250
+ try {
251
+ Method getReactDelegateMethod = currentActivity.getClass().getMethod("getReactDelegate");
252
+ return (ReactDelegate) getReactDelegateMethod.invoke(currentActivity);
253
+ } catch (Exception e) {
254
+ // RN < 0.74 doesn't have getReactDelegate method
255
+ return null;
256
+ }
257
+ }
258
+
259
+ private ReactHost resolveReactHost() {
260
+ ReactDelegate reactDelegate = resolveReactDelegate();
261
+ if (reactDelegate == null) {
262
+ return null;
263
+ }
264
+
265
+ try {
266
+ Field reactHostField = reactDelegate.getClass().getDeclaredField("mReactHost");
267
+ reactHostField.setAccessible(true);
268
+ return (ReactHost) reactHostField.get(reactDelegate);
269
+ } catch (Exception e) {
270
+ return null;
271
+ }
272
+ }
273
+
204
274
  // Use reflection to find the ReactInstanceManager. See #556 for a proposal for a less brittle way to approach this.
205
275
  private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldException, IllegalAccessException {
206
276
  ReactInstanceManager instanceManager = CodePush.getReactInstanceManager();
@@ -492,7 +562,7 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
492
562
  return null;
493
563
  }
494
564
  }
495
-
565
+
496
566
  promise.resolve("");
497
567
  } catch(CodePushUnknownException e) {
498
568
  CodePushUtils.log(e);
@@ -5,6 +5,7 @@
5
5
  #import <React/RCTEventDispatcher.h>
6
6
  #import <React/RCTRootView.h>
7
7
  #import <React/RCTUtils.h>
8
+ #import <React/RCTReloadCommand.h>
8
9
  #else // back compatibility for RN version < 0.40
9
10
  #import "RCTAssert.h"
10
11
  #import "RCTBridgeModule.h"
@@ -540,7 +541,7 @@ static NSString *const LatestRollbackCountKey = @"count";
540
541
  [super.bridge setValue:[CodePush bundleURL] forKey:@"bundleURL"];
541
542
  }
542
543
 
543
- [super.bridge reload];
544
+ RCTTriggerReloadCommandListeners(@"CodePush reload");
544
545
  });
545
546
  }
546
547
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bravemobile/react-native-code-push",
3
- "version": "9.0.0",
3
+ "version": "10.0.0-beta.0",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "main": "CodePush.js",
6
6
  "typings": "typings/react-native-code-push.d.ts",
@@ -4,7 +4,7 @@ module.exports = {
4
4
  android: {
5
5
  packageImportPath: "import com.microsoft.codepush.react.CodePush;",
6
6
  packageInstance:
7
- "new CodePush(getApplicationContext(), BuildConfig.DEBUG)",
7
+ "CodePush.getInstance(getApplicationContext(), BuildConfig.DEBUG)",
8
8
  sourceDir: './android/app'
9
9
  }
10
10
  }
@@ -1,37 +0,0 @@
1
- package com.microsoft.codepush.react;
2
-
3
- import android.content.Context;
4
-
5
- public class CodePushBuilder {
6
- private String mDeploymentKey;
7
- private Context mContext;
8
-
9
- private boolean mIsDebugMode;
10
- private String mServerUrl;
11
- private Integer mPublicKeyResourceDescriptor;
12
-
13
- public CodePushBuilder(Context context) {
14
- this.mDeploymentKey = "deprecated_deployment_key";
15
- this.mContext = context;
16
- this.mServerUrl = CodePush.getServiceUrl();
17
- }
18
-
19
- public CodePushBuilder setIsDebugMode(boolean isDebugMode) {
20
- this.mIsDebugMode = isDebugMode;
21
- return this;
22
- }
23
-
24
- public CodePushBuilder setServerUrl(String serverUrl) {
25
- this.mServerUrl = serverUrl;
26
- return this;
27
- }
28
-
29
- public CodePushBuilder setPublicKeyResourceDescriptor(int publicKeyResourceDescriptor) {
30
- this.mPublicKeyResourceDescriptor = publicKeyResourceDescriptor;
31
- return this;
32
- }
33
-
34
- public CodePush build() {
35
- return new CodePush(this.mContext, this.mIsDebugMode, this.mServerUrl, this.mPublicKeyResourceDescriptor);
36
- }
37
- }