dimenus opened a new issue, #1671:
URL: https://github.com/apache/cordova-ios/issues/1671

   ### Bug Report
   
   **Problem**
   
   Under the UIScene lifecycle (cordova-ios 8.x), a custom-scheme deep link is 
dropped on a **cold (killed-state)** launch — the global `window.handleOpenURL` 
is never called. The exact same link is delivered correctly when the app is 
already running (warm start). Android is unaffected.
   
   **Information**
   
   On a cold launch the open-URL notification is posted **before** 
`CDVHandleOpenURL` registers its observer, so `NSNotificationCenter` has no one 
to deliver it to and the URL is lost in native code (before it ever reaches JS).
   
   Sequence:
   
   1. `CDVSceneDelegate scene:willConnectToSession:options:` forwards 
`connectionOptions.URLContexts` to `scene:openURLContexts:`, which posts 
`CDVPluginHandleOpenURLNotification` synchronously during scene connection.
   2. `CDVHandleOpenURL` adds its observer only in `pluginInitialize`, which 
runs later — after the web view and plugins bootstrap.
   3. `NSNotificationCenter` delivers a notification only to observers present 
at post time. The observer is not registered yet, so 
`applicationLaunchedWithUrl:` never runs, `self.url` stays `nil`, and 
`applicationPageDidLoad:` later finds nothing to process.
   
   Instrumented NSLog timeline on the iOS 18.3 simulator (stock generated 
`SceneDelegate`, `class SceneDelegate: CDVSceneDelegate {}`):
   
   ```
   T+0.000  scene willConnectTo  urls=[myapp://path?...]   // posts 
CDVPluginHandleOpenURLNotification
   T+0.000  scene openURLContexts urls=[myapp://path?...]  // (forwarded by 
CDVSceneDelegate)
   T+0.139  CDVHandleOpenURL.pluginInitialize              // observer 
registered — ~139 ms too late
   T+1.171  CDVHandleOpenURL.applicationPageDidLoad hasUrl=0
   ```
   
   This used to work under the pre-scene `AppDelegate` model because 
`application:openURL:options:` was called after 
`didFinishLaunchingWithOptions:` (the plugin observer already existed). Moving 
URL delivery into `scene:willConnectToSession:` placed it before plugin 
initialization.
   
   **Command or Code**
   
   ```sh
   xcrun simctl uninstall <udid> com.example.app   # ensure cold / not running
   xcrun simctl install   <udid> "App.app"
   xcrun simctl openurl   <udid> "myscheme://path?foo=bar"
   # window.handleOpenURL is NOT called. With the app already running, it IS 
called.
   ```
   
   **Environment**
   
   - cordova-ios: 8.1.0
   - Engine: CDVWKWebViewEngine (WKWebView)
   - iOS: 18.3 simulator; reproduces on scene-based apps (iOS 13+)
   - Xcode: 26.x
   - A custom scheme registered in `Info.plist` (`CFBundleURLSchemes`) and a 
global `window.handleOpenURL`.
   
   **Steps to reproduce**
   
   1. Create a cordova-ios 8.1.0 app with a custom scheme and a global 
`window.handleOpenURL`.
   2. Install on an iOS 13+ device/simulator and **fully kill** the app.
   3. Open `myscheme://path?foo=bar` (e.g. `xcrun simctl openurl`, or tap a 
custom-scheme link in Safari and choose "Open").
   4. `handleOpenURL` does not fire. Repeat with the app already running — it 
fires.
   
   **Suggested fix**
   
   Buffer the launch URL so it survives until the observer exists. Either:
   
   - In `CDVSceneDelegate`, retain the cold-launch URL from 
`scene:willConnectToSession:` and re-post `CDVPluginHandleOpenURLNotification` 
on `CDVPageDidLoadNotification` (after the observer is registered), or
   - Have `CDVHandleOpenURL` / CordovaLib hold the most recent launch URL and 
replay it when the plugin initializes.
   
   App-side workaround that resolves it today — a custom `SceneDelegate` that 
buffers and re-posts on `CDVPageDidLoad`:
   
   ```swift
   import Cordova
   
   class SceneDelegate: CDVSceneDelegate {
       private var coldLaunchUrl: URL?
   
       override func scene(_ scene: UIScene, willConnectTo session: 
UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
           super.scene(scene, willConnectTo: session, options: 
connectionOptions)
           guard let url = connectionOptions.urlContexts.first?.url else { 
return }
           coldLaunchUrl = url
           NotificationCenter.default.addObserver(self, selector: 
#selector(redeliver),
               name: NSNotification.Name("CDVPageDidLoadNotification"), object: 
nil)
       }
   
       @objc private func redeliver() {
           NotificationCenter.default.removeObserver(self, name: 
NSNotification.Name("CDVPageDidLoadNotification"), object: nil)
           guard let url = coldLaunchUrl else { return }
           coldLaunchUrl = nil
           DispatchQueue.main.async {
               NotificationCenter.default.post(name: 
NSNotification.Name("CDVPluginHandleOpenURLNotification"), object: url)
           }
       }
   }
   ```
   
   A companion early JS buffer (define `window.handleOpenURL` before the app 
framework boots and drain it when ready) is also needed, because the 
re-delivered URL can still arrive before the page's `deviceready` handler 
assigns the real handler.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to