From 02e6e3427db5ebd4af26f2cee8264c16ff40abfe Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Fri, 26 Jun 2026 21:14:59 +0800 Subject: [PATCH] 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. --- src/main.capacitor.ts | 79 +++---------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) diff --git a/src/main.capacitor.ts b/src/main.capacitor.ts index fa1c6b44..16be9559 100644 --- a/src/main.capacitor.ts +++ b/src/main.capacitor.ts @@ -471,71 +471,6 @@ logger.log("[Capacitor] 🚀 Mounting app"); app.mount("#app"); 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 { - 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 { - // 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) // 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 @@ -550,15 +485,11 @@ if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === "android") { }, delay); }); } else if (Capacitor.isNativePlatform() && Capacitor.getPlatform() === "ios") { - // Phase 2A: replace the fixed 1000ms delay with a deterministic wait for the - // SharedImage plugin. The very first startup check runs only after the plugin - // is confirmed available; if the bounded retry budget is exhausted we still - // attempt once as a best-effort fallback (appStateChange retries on the next - // activation), so startup is never worse than the previous fixed-delay path. - void (async () => { - await waitForSharedImagePluginReady(); - await checkForSharedImageAndNavigate(); - })(); + // Phase 2B-3: the SharedImage plugin is now registered deterministically from + // AppBridgeViewController.capacitorDidLoad() before the web layer loads, so it + // is guaranteed to exist here. Perform the initial shared-image check + // immediately without waiting/polling for plugin readiness. + void checkForSharedImageAndNavigate(); } // Listen for app state changes to detect when app becomes active