Skip to content

Commit 8ef3f9f

Browse files
authored
fix(Expo): fix app stuck on splash screen in SDK 54 (#103)
* fix(Expo): fix app stuck on splash screen in SDK 54 * test(Expo): add tests for withAndroidMainApplicationDependency
1 parent 8a4aecd commit 8ef3f9f

File tree

3 files changed

+189
-2
lines changed

3 files changed

+189
-2
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
2+
3+
jest.mock(
4+
"expo/config-plugins",
5+
() => ({
6+
withMainApplication: jest.fn((config, apply) =>
7+
// @ts-expect-error -- mocking
8+
apply({ modResults: { contents: config._contents ?? "" } })
9+
),
10+
WarningAggregator: {
11+
addWarningAndroid: jest.fn(),
12+
},
13+
}),
14+
{ virtual: true }
15+
);
16+
17+
const { withMainApplication, WarningAggregator } = require("expo/config-plugins");
18+
const { withAndroidMainApplicationDependency } = require("../withCodePushAndroid.js");
19+
20+
// https://github.com/expo/expo/blob/deeaccf50bbc5b904c0b67c120efcf7e0accfd0b/templates/expo-template-bare-minimum/android/app/src/main/java/com/helloworld/MainApplication.kt
21+
const expo54Template = `
22+
package com.helloworld
23+
24+
import android.app.Application
25+
import android.content.res.Configuration
26+
27+
import com.facebook.react.PackageList
28+
import com.facebook.react.ReactApplication
29+
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
30+
import com.facebook.react.ReactNativeHost
31+
import com.facebook.react.ReactPackage
32+
import com.facebook.react.ReactHost
33+
import com.facebook.react.common.ReleaseLevel
34+
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
35+
import com.facebook.react.defaults.DefaultReactNativeHost
36+
37+
import expo.modules.ApplicationLifecycleDispatcher
38+
import expo.modules.ReactNativeHostWrapper
39+
40+
class MainApplication : Application(), ReactApplication {
41+
42+
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
43+
this,
44+
object : DefaultReactNativeHost(this) {
45+
override fun getPackages(): List<ReactPackage> =
46+
PackageList(this).packages.apply {
47+
// Packages that cannot be autolinked yet can be added manually here, for example:
48+
// add(MyReactNativePackage())
49+
}
50+
51+
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
52+
53+
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
54+
55+
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
56+
}
57+
)
58+
59+
override val reactHost: ReactHost
60+
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
61+
62+
override fun onCreate() {
63+
super.onCreate()
64+
DefaultNewArchitectureEntryPoint.releaseLevel = try {
65+
ReleaseLevel.valueOf(BuildConfig.REACT_NATIVE_RELEASE_LEVEL.uppercase())
66+
} catch (e: IllegalArgumentException) {
67+
ReleaseLevel.STABLE
68+
}
69+
loadReactNative(this)
70+
ApplicationLifecycleDispatcher.onApplicationCreate(this)
71+
}
72+
73+
override fun onConfigurationChanged(newConfig: Configuration) {
74+
super.onConfigurationChanged(newConfig)
75+
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
76+
}
77+
}
78+
`;
79+
80+
// https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/main/java/com/helloworld/MainApplication.kt
81+
const expo55Template = `
82+
package com.helloworld
83+
84+
import android.app.Application
85+
import android.content.res.Configuration
86+
87+
import com.facebook.react.PackageList
88+
import com.facebook.react.ReactApplication
89+
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
90+
import com.facebook.react.ReactPackage
91+
import com.facebook.react.ReactHost
92+
import com.facebook.react.common.ReleaseLevel
93+
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
94+
95+
import expo.modules.ApplicationLifecycleDispatcher
96+
import expo.modules.ExpoReactHostFactory
97+
98+
class MainApplication : Application(), ReactApplication {
99+
100+
override val reactHost: ReactHost by lazy {
101+
ExpoReactHostFactory.getDefaultReactHost(
102+
context = applicationContext,
103+
packageList =
104+
PackageList(this).packages.apply {
105+
// Packages that cannot be autolinked yet can be added manually here, for example:
106+
// add(MyReactNativePackage())
107+
}
108+
)
109+
}
110+
111+
override fun onCreate() {
112+
super.onCreate()
113+
DefaultNewArchitectureEntryPoint.releaseLevel = try {
114+
ReleaseLevel.valueOf(BuildConfig.REACT_NATIVE_RELEASE_LEVEL.uppercase())
115+
} catch (e: IllegalArgumentException) {
116+
ReleaseLevel.STABLE
117+
}
118+
loadReactNative(this)
119+
ApplicationLifecycleDispatcher.onApplicationCreate(this)
120+
}
121+
122+
override fun onConfigurationChanged(newConfig: Configuration) {
123+
super.onConfigurationChanged(newConfig)
124+
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
125+
}
126+
}
127+
`;
128+
129+
const withMainApplicationMock = withMainApplication as jest.Mock;
130+
const addWarningAndroidMock = WarningAggregator.addWarningAndroid as jest.Mock;
131+
132+
describe("withAndroidMainApplicationDependency", () => {
133+
beforeEach(() => {
134+
withMainApplicationMock.mockClear();
135+
addWarningAndroidMock.mockClear();
136+
});
137+
138+
it("adds CodePush import and method override in Expo SDK 53", () => {
139+
const result = withAndroidMainApplicationDependency({
140+
_contents: expo54Template,
141+
sdkVersion: "53.0.0",
142+
});
143+
144+
const modifiedContent = result.modResults.contents;
145+
146+
expect(modifiedContent).toContain("import com.microsoft.codepush.react.CodePush");
147+
expect(modifiedContent).toContain("override fun getJSBundleFile(): String = CodePush.getJSBundleFile()");
148+
});
149+
150+
it("adds CodePush import and method override with getInstance() in Expo SDK 54", () => {
151+
const result = withAndroidMainApplicationDependency({
152+
_contents: expo54Template,
153+
sdkVersion: "54.0.0",
154+
});
155+
156+
const modifiedContent = result.modResults.contents;
157+
158+
expect(modifiedContent).toContain("import com.microsoft.codepush.react.CodePush");
159+
expect(modifiedContent).toContain("CodePush.getInstance(applicationContext, BuildConfig.DEBUG)");
160+
expect(modifiedContent).toContain("return CodePush.getJSBundleFile()");
161+
});
162+
163+
it("adds CodePush import and jsBundleFilePath argument in Expo SDK 55", () => {
164+
const result = withAndroidMainApplicationDependency({
165+
_contents: expo55Template,
166+
sdkVersion: "55.0.0",
167+
});
168+
169+
const modifiedContent = result.modResults.contents;
170+
171+
expect(modifiedContent).toContain("import com.microsoft.codepush.react.CodePush");
172+
expect(modifiedContent).toContain("jsBundleFilePath = CodePush.getJSBundleFile()");
173+
174+
const packageListIndex = modifiedContent.indexOf("packageList =");
175+
const jsBundleIndex = modifiedContent.indexOf("jsBundleFilePath = CodePush.getJSBundleFile()");
176+
expect(jsBundleIndex).toBeGreaterThan(packageListIndex);
177+
});
178+
});

expo/plugin/withCodePushAndroid.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,19 @@ const withAndroidMainApplicationDependency = (config) => {
6868
if (action.modResults.contents.includes(RN_082_MARKER)) {
6969
action.modResults.contents = addJsBundleFilePathArgument(action.modResults.contents);
7070
} else {
71+
// https://github.com/Soomgo-Mobile/react-native-code-push/issues/97
72+
const isExpoSDK54 = config.sdkVersion?.startsWith('54.') ?? false;
73+
const addingCode = isExpoSDK54
74+
? ' override fun getJSBundleFile(): String {\n' +
75+
' CodePush.getInstance(applicationContext, BuildConfig.DEBUG)\n' +
76+
' return CodePush.getJSBundleFile()\n' +
77+
' }\n'
78+
: ' override fun getJSBundleFile(): String = CodePush.getJSBundleFile()\n';
7179
action.modResults.contents = androidMainApplicationApplyImplementation(
7280
action.modResults.contents,
7381
'object : DefaultReactNativeHost(this) {',
74-
' override fun getJSBundleFile(): String = CodePush.getJSBundleFile()\n');
82+
addingCode,
83+
);
7584
}
7685
}
7786

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"prepack": "npm run build:cli",
7070
"publish": "npm publish --access=public",
7171
"eslint": "eslint --quiet .",
72-
"jest": "jest src/versioning/* && npm run --workspace cli test"
72+
"jest": "jest src/versioning/* expo/* && npm run --workspace cli test"
7373
},
7474
"repository": {
7575
"type": "git",

0 commit comments

Comments
 (0)