From ce8720663b4258c3c430772dce91724900c16d96 Mon Sep 17 00:00:00 2001 From: Cort Fritz Date: Sun, 6 Sep 2026 22:06:40 -0700 Subject: [PATCH] Present alerts on the window that exists, not the first one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nif_alert_show and nif_action_sheet_show resolve their presenting controller through a private root_vc() that takes `scene.windows.firstObject` and reads its rootViewController. That assumes the app's window sorts first in the scene's window set. On iOS 26 it does not: the property comes back nil, root_vc() returns nil, the `if (vc)` guard drops the dialog, and nothing is logged. An alert simply never appears — on a physical iPhone 16 Pro (iOS 26.6.1) no confirm dialog could be shown at all. Core already has the right lookup in mob_root_vc, which tries `keyWindow` first and falls back to windows.firstObject, and the scanner and camera plugins each carry a copy of it (scan_root_vc, cam_root_vc). Those present fine on the same device and the same scene, which is what isolates the window lookup as the difference. Route root_vc through mob_root_vc and keep only the part that was doing real work — walking to the topmost presented controller. The activationState == ForegroundActive filter goes with it, matching mob_root_vc and the two plugin copies. --- ios/mob_nif.m | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ios/mob_nif.m b/ios/mob_nif.m index 1f6740d..86b5d58 100644 --- a/ios/mob_nif.m +++ b/ios/mob_nif.m @@ -7295,18 +7295,20 @@ static void mob_deliver_alert_action(const char *action) { enif_free_env(env); } -// Returns the root UIViewController for presenting dialogs. +// Returns the topmost presented UIViewController for presenting dialogs. +// +// The window has to be resolved through mob_root_vc, not by hand. Taking +// `scene.windows.firstObject` and reading its rootViewController assumes the +// app's window sorts first in the scene's window set; on iOS 26 it does not, +// the property is nil, and every alert and action sheet is dropped with no +// error anywhere. mob_root_vc tries `keyWindow` first and only then falls back +// — which is why the scanner and camera plugins (scan_root_vc, cam_root_vc, +// both copies of it) present fine while these two did not. static UIViewController *root_vc(void) { - for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) { - if (scene.activationState == UISceneActivationStateForegroundActive) { - UIWindow *win = scene.windows.firstObject; - UIViewController *vc = win.rootViewController; - while (vc.presentedViewController) - vc = vc.presentedViewController; - return vc; - } - } - return nil; + UIViewController *vc = mob_root_vc(); + while (vc.presentedViewController) + vc = vc.presentedViewController; + return vc; } // ── NIF: alert_show/3 ────────────────────────────────────────────────────────