Skip to content

Conversation

@simplysparsh
Copy link

@simplysparsh simplysparsh commented Jan 20, 2026

Summary

  • use an active UIWindowScene when creating the temp presentation window on iOS 13+
  • fall back to UIWindow(frame:) when no scene is available

Reason for patch:

iOS 13+ requires modal presentation from a UIWindow attached to an active UIWindowScene. Capacitor’s presentVC used UIWindow(frame:), which isn’t scene‑attached, so SFSafariViewController (via @capacitor/browser) could fail to appear. We switched the temp window to use the foreground UIWindowScene when available, with a fallback to UIWindow(frame:) for older iOS or if no scene is active.

Fix

We updated CapacitorBridge.presentVC to:

  • Look up the foreground active UIWindowScene on iOS 13+
  • Create the temp window with UIWindow(windowScene:)
  • Fall back to UIWindow(frame:) when no scene is available (or on iOS < 13)

This keeps the presentation path correct for multi‑scene iOS and stabilizes Safari-based flows.

Scope

The change is in @capacitor/ios core (not a plugin), but it benefits any plugin that presents view controllers via the bridge, especially @capacitor/browser.

Use UIWindowScene for temp presentation windows on iOS 13+ to ensure view
controllers like SFSafariViewController are presented from an active scene.
Copy link

@de-dan de-dan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the fallback is necessary, since the minimum iOS version is v15 with Capacitor 8.

@simplysparsh
Copy link
Author

simplysparsh commented Jan 21, 2026

I don't think the fallback is necessary, since the minimum iOS version is v15 with Capacitor 8.

Capacitor 8’s minimum iOS is 15, but the fallback isn’t about older iOS versions—it’s about scene availability. Even on iOS 15, connectedScenes can be empty or not foregroundActive during app transitions, so creating a UIWindow(windowScene:) can fail. The fallback keeps presentation working in those edge states. It’s a defensive guard with no downside and aligns with Apple’s guidance to present from an active scene when available

@de-dan

@simplysparsh

This comment was marked as abuse.

@simplysparsh simplysparsh requested a review from de-dan January 21, 2026 01:36
Per review feedback: since Capacitor 8 requires iOS 15+, the
#available(iOS 13.0, *) check is unnecessary. Simplified to a
single-line assignment while keeping the frame fallback for
edge cases where no active scene is available.
@simplysparsh
Copy link
Author

simplysparsh commented Jan 21, 2026

@de-dan Thanks for the review! You're right, i didn't catch your first part initially. Since Capacitor 8 requires iOS 15+, the #available(iOS 13.0, *) check was unnecessary.

I've simplified the code to remove that check. I kept a minimal fallback (?? UIWindow(frame:)) for the edge case where no active UIWindowScene is found, but the nested conditionals are gone.

The change is now a single-line assignment:

self.tmpWindow = windowScene.map { UIWindow(windowScene: $0) } ?? UIWindow(frame: UIScreen.main.bounds)

Let me know if this looks good!

@simplysparsh

This comment was marked as abuse.

@theproducer theproducer self-assigned this Jan 21, 2026
@theproducer theproducer requested review from a team and removed request for de-dan January 21, 2026 16:31
@theproducer theproducer requested a review from a team January 21, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants