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]