From adb8177f2f5d0ffd25229ae4fb42deb66bb5418d Mon Sep 17 00:00:00 2001 From: Sylvain Bettinelli Date: Tue, 22 Sep 2026 09:16:08 +0000 Subject: [PATCH] =?UTF-8?q?=C3=89cran=20noir=20au=20lancement=20:=20la=20f?= =?UTF-8?q?en=C3=AAtre=20n'=C3=A9tait=20jamais=20rendue=20visible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La première version conditionnait la création de la fenêtre à `window == nil`, par prudence : le manifeste déclarant UISceneStoryboardFile = Main, je supposais qu'UIKit avait déjà monté la fenêtre depuis le storyboard et qu'il ne fallait pas l'écraser. Sur appareil, le résultat est un écran noir permanent, sans erreur, sans log, sans crash. UIKit fournit bien un objet fenêtre, mais ne le rend pas visible à notre place : la garde sautait donc makeKeyAndVisible() et rien ne s'affichait jamais. Le fond noir de capacitor.config.ts rendait le symptôme indiscernable d'un écran d'attente. Le template de Capacitor crée la fenêtre sans condition. C'est le chemin éprouvé, et s'en écarter « par sécurité » aura coûté un aller-retour de build. Le seul écart qui reste nécessaire est MainViewController au lieu d'un CAPBridgeViewController nu : notre sous-classe enregistre les neuf plugins in-app dans capacitorDidLoad(), et ne dépend de rien du storyboard. ⚠️ À retenir pour la suite : sur ce chemin, une garde défensive ne protège de rien et masque le défaut qu'elle prétend éviter. La condition de succès était observable (une fenêtre visible), pas déductible. Co-Authored-By: Claude Opus 5 (1M context) --- ios/App/App/SceneDelegate.swift | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/ios/App/App/SceneDelegate.swift b/ios/App/App/SceneDelegate.swift index 367209d..8a90ab1 100644 --- a/ios/App/App/SceneDelegate.swift +++ b/ios/App/App/SceneDelegate.swift @@ -33,21 +33,24 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } - // Le manifeste déclare `UISceneStoryboardFile = Main` : UIKit a donc - // déjà chargé Main.storyboard et instancié `MainViewController`, qui - // porte l'enregistrement des plugins natifs et la config WKWebView. + // ⚠️ Ne PAS conditionner cette création à `window == nil`. // - // Le template de Capacitor pose ici un `CAPBridgeViewController()` nu. - // L'appliquer tel quel remplacerait notre sous-classe et ferait perdre - // cette configuration **sans erreur visible**. On ne crée donc la - // fenêtre que si le storyboard ne l'a pas fait, et avec notre - // contrôleur. - if window == nil { - let fenetre = UIWindow(windowScene: windowScene) - fenetre.rootViewController = MainViewController() - window = fenetre - fenetre.makeKeyAndVisible() - } + // C'était ma première version, par prudence : le manifeste déclarant + // `UISceneStoryboardFile = Main`, je supposais qu'UIKit avait déjà + // monté la fenêtre et qu'il ne fallait pas l'écraser. Résultat sur + // appareil : **écran noir permanent, sans la moindre erreur**. UIKit + // fournit bien une fenêtre, mais ne la rend pas visible à notre place ; + // la garde sautait donc `makeKeyAndVisible()` et rien ne s'affichait. + // + // Le template de Capacitor crée la fenêtre sans condition, et c'est le + // chemin éprouvé. Le seul écart nécessaire est `MainViewController` au + // lieu d'un `CAPBridgeViewController` nu : notre sous-classe enregistre + // les neuf plugins in-app dans `capacitorDidLoad()`. Elle ne dépend de + // rien du storyboard, donc l'instancier ici est sans perte. + let fenetre = UIWindow(windowScene: windowScene) + fenetre.rootViewController = MainViewController() + window = fenetre + fenetre.makeKeyAndVisible() SceneDelegateProxy.shared.scene(scene, willConnectTo: session, options: connectionOptions) }