Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Example/expoUsePushy/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
},
"web": {
"favicon": "./assets/favicon.png"
}
},
"plugins": [
"./plugins/with-rct-bridge-bridging-header",
Comment on lines +29 to +30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Point the plugin entry at the file added in this PR.

app.plugin.js is added at the repo root, but Line 30 registers ./plugins/with-rct-bridge-bridging-header. Unless another file exists at that exact path, Expo will fail to resolve the plugin and the bridging-header patch never runs.

Suggested fix
     "plugins": [
-      "./plugins/with-rct-bridge-bridging-header",
+      "../../app.plugin.js",
       "expo-font",
       "expo-router",
       "expo-web-browser"
     ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"plugins": [
"./plugins/with-rct-bridge-bridging-header",
"plugins": [
"../../app.plugin.js",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Example/expoUsePushy/app.json` around lines 29 - 30, The Expo plugin entry is
pointing to the wrong module, so the bridging-header patch will not be loaded.
Update the plugin reference in app.json to use the new app.plugin.js added at
the repo root, and verify the plugin name/path matches the actual file exported
for the Expo config plugin.

"expo-font",
"expo-router",
"expo-web-browser"
]
}
}
2,632 changes: 1,625 additions & 1,007 deletions Example/expoUsePushy/bun.lock

Large diffs are not rendered by default.

55 changes: 28 additions & 27 deletions Example/expoUsePushy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,41 @@
"lint": "expo lint"
},
"dependencies": {
"@expo/vector-icons": "^14.1.0",
"@expo/vector-icons": "^15.0.3",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6",
"expo": "~53.0.22",
"expo-blur": "~14.1.5",
"expo-constants": "~17.1.7",
"expo-font": "~13.3.2",
"expo-haptics": "~14.1.4",
"expo-image": "~2.4.0",
"expo-linking": "~7.1.7",
"expo-router": "~5.1.5",
"expo-splash-screen": "~0.30.10",
"expo-status-bar": "~2.2.3",
"expo-symbols": "~0.4.5",
"expo-system-ui": "~5.0.11",
"expo-web-browser": "~14.2.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.6",
"react-native-gesture-handler": "~2.24.0",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1",
"react-native-update": "^10.34.4",
"react-native-web": "~0.20.0",
"react-native-webview": "13.13.5"
"expo": "^54.0.0",
"expo-blur": "~15.0.8",
"expo-constants": "~18.0.13",
"expo-font": "~14.0.12",
"expo-haptics": "~15.0.8",
"expo-image": "~3.0.11",
"expo-linking": "~8.0.12",
"expo-router": "~6.0.24",
"expo-splash-screen": "~31.0.13",
"expo-status-bar": "~3.0.9",
"expo-symbols": "~1.0.8",
"expo-system-ui": "~6.0.9",
"expo-web-browser": "~15.0.11",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.5",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-update": "^10.42.6",
"react-native-web": "^0.21.0",
"react-native-webview": "13.15.0",
"react-native-worklets": "0.5.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.10",
"typescript": "~5.8.3",
"@types/react": "~19.1.10",
"eslint": "^9.25.0",
"eslint-config-expo": "~9.2.0"
"eslint-config-expo": "~10.0.0",
"typescript": "~5.9.2"
},
"private": true
}
43 changes: 43 additions & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const fs = require("fs");
const path = require("path");

function requireConfigPlugins() {
try {
return require("@expo/config-plugins");
} catch {
return require(require.resolve("@expo/config-plugins", {
paths: [process.cwd()],
}));
}
}

const { withDangerousMod } = requireConfigPlugins();

const RCT_BRIDGE_IMPORT = "#import <React/RCTBridge.h>";

function withPushyRCTBridgeImport(config) {
return withDangerousMod(config, [
"ios",
(config) => {
const { platformProjectRoot, projectName } = config.modRequest;
const bridgingHeaderPath = path.join(
platformProjectRoot,
projectName,
`${projectName}-Bridging-Header.h`
);

const contents = fs.readFileSync(bridgingHeaderPath, "utf8");

if (!contents.includes(RCT_BRIDGE_IMPORT)) {
fs.writeFileSync(
bridgingHeaderPath,
`${contents.trimEnd()}\n\n${RCT_BRIDGE_IMPORT}\n`
);
}

return config;
},
]);
}

module.exports = withPushyRCTBridgeImport;
18 changes: 15 additions & 3 deletions harmony/pushy/src/main/ets/UpdateContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export class UpdateContext {
private static DEBUG: boolean = false;
private static isUsingBundleUrl: boolean = false;
private static ignoreRollback: boolean = false;
private static cachedPackageVersion: string = '';
private static cachedBuildTime: string = '';
Comment on lines +29 to +30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

This still allows the first background-thread read to hit the unsafe sync APIs.

The cache is only populated lazily, so if the first UpdateContext/getConstants() call happens on the JS background thread, Lines 68-72 and especially Lines 84-91 still do the synchronous lookups before anything is cached. That means getBuildTime() can still throw, return '', and then PushyTurboModule.getConstants() will pass that empty build time into syncStateWithBinaryVersion(), preserving the same preference-clearing path described in the PR objective. This needs a guaranteed UI-thread warm-up step before any background access, or the background path must avoid touching meta.json when the cache is empty.

Also applies to: 65-78, 80-96

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harmony/pushy/src/main/ets/UpdateContext.ts` around lines 29 - 30, The lazy
caching in UpdateContext still lets the first background-thread call reach the
unsafe synchronous metadata reads, so warm the cache on the UI thread before any
JS background access or make the background path avoid reading meta.json when
the cache is empty. Update the UpdateContext/getConstants flow and the
getBuildTime()/getPackageVersion() logic so PushyTurboModule.getConstants()
never falls back to empty build-time values or triggers
syncStateWithBinaryVersion() with an uninitialized cache.


constructor(context: common.UIAbilityContext) {
this.context = context;
Expand Down Expand Up @@ -60,28 +62,38 @@ export class UpdateContext {
}

public getPackageVersion(): string {
if (UpdateContext.cachedPackageVersion) {
return UpdateContext.cachedPackageVersion;
}
try {
const bundleInfo = bundleManager.getBundleInfoForSelfSync(
this.getBundleFlags(),
);
return bundleInfo?.versionName || 'Unknown';
UpdateContext.cachedPackageVersion = bundleInfo?.versionName || 'Unknown';
return UpdateContext.cachedPackageVersion;
} catch (error) {
console.error('Failed to get bundle info:', error);
return '';
}
}

public getBuildTime(): string {
if (UpdateContext.cachedBuildTime) {
return UpdateContext.cachedBuildTime;
}
try {
const content =
this.context.resourceManager.getRawFileContentSync('meta.json');
const metaData = JSON.parse(
new util.TextDecoder().decodeToString(content),
) as Record<string, string | number | boolean | null | undefined>;
if (metaData.pushy_build_time) {
return String(metaData.pushy_build_time);
UpdateContext.cachedBuildTime = String(metaData.pushy_build_time);
return UpdateContext.cachedBuildTime;
}
} catch {}
} catch (error) {
console.error('Failed to read build time from raw file:', error);
}
return '';
}

Expand Down
1 change: 0 additions & 1 deletion src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,4 @@ log('bootup status', {
isFirstTimeDebug,
isDebugChannel,
cInfo,
uuid,
});
Loading