Bug Report
Capacitor Plugin(s): @capacitor/browser@8.0.3
Capacitor Version: @capacitor/core@8.3.1, @capacitor/ios@8.3.1
Platform / OS: iOS 18.6 (simulator iPhone 16 Plus, also reproduced on physical iPhone 13 mini iOS 18.6)
Xcode: 26.x (also occurs with Xcode 16.x)
Description
When Browser.open({ url }) is called with the default presentationStyle: 'fullscreen', the SFSafariViewController modal presentation detaches the underlying CAPBridgeViewController.view from its window during presentation, then reattaches it on dismiss. During this detach/reattach cycle, the underlying WKWebView's internal WKContentView becomes stuck at frame=(0, 0, 0, 0), while the outer WKWebView frame remains correct. The result is a fully blank/white screen after dismissing the in-app browser, with no possible recovery via webView.reload(), setNeedsLayout, or layoutIfNeeded.
This affects any Capacitor app that uses Browser.open with default options. The bug is most easily observed when the URL triggers a Universal Link hand-off (e.g. https://apps.apple.com/... → App Store hand-off) because the dismiss happens automatically, but it can also be reproduced with regular URLs by manual dismiss.
Reproduction
- Build a minimal Capacitor 8 app with
@capacitor/browser installed
- Add a button that calls
Browser.open({ url: 'https://apps.apple.com/kr/app/heri2go-clinic/id6767798415' }) (any Universal Link URL works)
- Tap the button, observe SFSafariViewController fullscreen modal
- Wait for iOS to hand off to App Store (or manually dismiss after a few seconds)
- Return to the app
- Underlying
WKWebView is blank/white
Workaround
Pass presentationStyle: 'popover' to Browser.open. iPhone falls back to pageSheet, which does not detach the underlying view from its window, so WKContentView is not squeezed.
await Browser.open({ url, presentationStyle: 'popover' });
Detailed log evidence (xcrun simctl spawn ... log show ...)
Timeline (PID 53206 in our reproduction):
23:24:55.715 Browser.open called → SFSafariViewController being presented
23:24:57.486 WKContentView frame = (0 0; 430 873) ← still normal at this moment
23:24:58.043 CAPBridgeViewController.viewDidAppear fires after dismiss
WKContentView frame = (0 0; 0 0) ← squeezed
23:24:58.043 setNeedsLayout / layoutIfNeeded called → no effect
23:24:58.148 webView.reload() + load(URLRequest) called → no effect
23:24:58.306 WebPageProxy::didCommitLoadForFrame
WKContentView frame = (0 0; 0 0) ← still squeezed after reload
The WKContentView instance pointer stays the same throughout, so it's not a fresh content view replacing an old one — the existing one is being resized to zero by some internal WebKit layout pass during the modal dismiss cycle.
Why we're reporting
We hit this in production after our app integrated linkified URLs in chat messages. The default behavior of Browser.open on iOS produces a non-recoverable blank screen for users who tap any external link. The popover workaround works but seems like an unexpected default; the documented and intuitive choice (fullscreen) silently breaks the host app's WKWebView.
A proper fix likely needs to be in the iOS implementation of @capacitor/browser — either:
- Avoid the underlying view detach during
present(_:animated:) (use a separate presentation context), or
- Force a layout pass on the host view's WKWebView after the SFSafariViewController is dismissed.
Happy to provide a minimal reproduction repo if helpful.
Bug Report
Capacitor Plugin(s):
@capacitor/browser@8.0.3Capacitor Version:
@capacitor/core@8.3.1,@capacitor/ios@8.3.1Platform / OS: iOS 18.6 (simulator iPhone 16 Plus, also reproduced on physical iPhone 13 mini iOS 18.6)
Xcode: 26.x (also occurs with Xcode 16.x)
Description
When
Browser.open({ url })is called with the defaultpresentationStyle: 'fullscreen', theSFSafariViewControllermodal presentation detaches the underlyingCAPBridgeViewController.viewfrom its window during presentation, then reattaches it on dismiss. During this detach/reattach cycle, the underlyingWKWebView's internalWKContentViewbecomes stuck atframe=(0, 0, 0, 0), while the outerWKWebViewframe remains correct. The result is a fully blank/white screen after dismissing the in-app browser, with no possible recovery viawebView.reload(),setNeedsLayout, orlayoutIfNeeded.This affects any Capacitor app that uses
Browser.openwith default options. The bug is most easily observed when the URL triggers a Universal Link hand-off (e.g.https://apps.apple.com/...→ App Store hand-off) because the dismiss happens automatically, but it can also be reproduced with regular URLs by manual dismiss.Reproduction
@capacitor/browserinstalledBrowser.open({ url: 'https://apps.apple.com/kr/app/heri2go-clinic/id6767798415' })(any Universal Link URL works)WKWebViewis blank/whiteWorkaround
Pass
presentationStyle: 'popover'toBrowser.open. iPhone falls back topageSheet, which does not detach the underlying view from its window, soWKContentViewis not squeezed.Detailed log evidence (
xcrun simctl spawn ... log show ...)Timeline (PID 53206 in our reproduction):
The
WKContentViewinstance pointer stays the same throughout, so it's not a fresh content view replacing an old one — the existing one is being resized to zero by some internal WebKit layout pass during the modal dismiss cycle.Why we're reporting
We hit this in production after our app integrated linkified URLs in chat messages. The default behavior of
Browser.openon iOS produces a non-recoverable blank screen for users who tap any external link. Thepopoverworkaround works but seems like an unexpected default; the documented and intuitive choice (fullscreen) silently breaks the host app's WKWebView.A proper fix likely needs to be in the iOS implementation of
@capacitor/browser— either:present(_:animated:)(use a separate presentation context), orHappy to provide a minimal reproduction repo if helpful.