refactor(ios): remove SharedImage plugin-readiness polling (Phase 2B-3)

The SharedImage plugin is now registered deterministically from
AppBridgeViewController.capacitorDidLoad() before the web layer loads, so
JS no longer needs to wait for it. Remove the readiness machinery that
existed solely to tolerate the old async AppDelegate registration:
waitForSharedImagePluginReady(), the sleep() helper, the
STARTUP_PLUGIN_MAX_ATTEMPTS/RETRY_DELAY_MS constants and retry loop, and
the three readiness-only console diagnostics (waiting / ready after N /
giving up).

The iOS startup branch now calls checkForSharedImageAndNavigate()
immediately. All other share-target behavior is unchanged: cold-start,
extension, and launch diagnostics; native trace APIs; the Share Target
Debug Panel; appStateChange/appUrlOpen handling; actual shared-image
handling; and the Android startup path. No JS readiness diagnostics
remain; no Android changes.
This commit is contained in:
Jose Olarte III
2026-06-26 21:14:59 +08:00
parent 337a8f7536
commit 02e6e3427d

View File

@@ -471,71 +471,6 @@ logger.log("[Capacitor] 🚀 Mounting app");
app.mount("#app"); app.mount("#app");
logger.info(`[Main] ✅ App mounted successfully`); logger.info(`[Main] ✅ App mounted successfully`);
/**
* Phase 2A: deterministic startup readiness for the SharedImage plugin (iOS).
*
* The native SharedImage plugin is registered asynchronously by AppDelegate
* (with its own retry budget), so a fixed startup delay cannot guarantee the
* plugin is reachable from JS before the first shared-image check. Instead of
* assuming readiness after an arbitrary sleep, we probe the plugin with a
* lightweight, read-only hasSharedImage() call and retry for a short bounded
* period until it responds or the budget is exhausted.
*
* This applies ONLY to the very first startup shared-image check. The
* appStateChange and appUrlOpen paths are intentionally left unchanged.
*/
const STARTUP_PLUGIN_MAX_ATTEMPTS = 10;
const STARTUP_PLUGIN_RETRY_DELAY_MS = 300;
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Wait until the iOS SharedImage native plugin is available.
*
* Probes the plugin with a read-only hasSharedImage() call; a successful
* resolution means the native plugin instance is registered and reachable.
* Retries up to STARTUP_PLUGIN_MAX_ATTEMPTS with STARTUP_PLUGIN_RETRY_DELAY_MS
* between attempts.
*
* @returns true once the plugin responds, false if the retry budget is exhausted
*/
async function waitForSharedImagePluginReady(): Promise<boolean> {
// TEMPORARY SHARE TARGET DIAGNOSTICS
console.info(
"[ShareTarget] Startup shared-image check waiting for SharedImage plugin",
);
for (let attempt = 1; attempt <= STARTUP_PLUGIN_MAX_ATTEMPTS; attempt++) {
try {
// Lightweight, read-only probe. hasSharedImage() does not consume or
// mutate the pending share, so probing is side-effect free.
await SharedImage.hasSharedImage();
// TEMPORARY SHARE TARGET DIAGNOSTICS
console.info(
`[ShareTarget] SharedImage plugin ready after ${attempt} attempt(s)`,
);
return true;
} catch (error) {
// Plugin not registered yet; wait and retry within the bounded budget.
logger.debug(
`[Main] SharedImage plugin not ready (attempt ${attempt}/${STARTUP_PLUGIN_MAX_ATTEMPTS}):`,
error instanceof Error ? error.message : String(error),
);
if (attempt < STARTUP_PLUGIN_MAX_ATTEMPTS) {
await sleep(STARTUP_PLUGIN_RETRY_DELAY_MS);
}
}
}
// TEMPORARY SHARE TARGET DIAGNOSTICS
console.info(
`[ShareTarget] Startup shared-image check giving up after ${STARTUP_PLUGIN_MAX_ATTEMPTS} attempt(s)`,
);
return false;
}
// Check for shared image on initial load (in case app was launched from share sheet) // Check for shared image on initial load (in case app was launched from share sheet)
// On Android, share intents are processed in MainActivity.onCreate, so we need to check // On Android, share intents are processed in MainActivity.onCreate, so we need to check
// after a delay to ensure the native code has finished processing // after a delay to ensure the native code has finished processing
@@ -550,15 +485,11 @@ if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === "android") {
}, delay); }, delay);
}); });
} else if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === "ios") { } else if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === "ios") {
// Phase 2A: replace the fixed 1000ms delay with a deterministic wait for the // Phase 2B-3: the SharedImage plugin is now registered deterministically from
// SharedImage plugin. The very first startup check runs only after the plugin // AppBridgeViewController.capacitorDidLoad() before the web layer loads, so it
// is confirmed available; if the bounded retry budget is exhausted we still // is guaranteed to exist here. Perform the initial shared-image check
// attempt once as a best-effort fallback (appStateChange retries on the next // immediately without waiting/polling for plugin readiness.
// activation), so startup is never worse than the previous fixed-delay path. void checkForSharedImageAndNavigate();
void (async () => {
await waitForSharedImagePluginReady();
await checkForSharedImageAndNavigate();
})();
} }
// Listen for app state changes to detect when app becomes active // Listen for app state changes to detect when app becomes active