[🐛] iOS 26.5: app frozen (touches dead) after a rewarded ad auto-dismisses or after backgrounding during the ad — adDidDismissFullScreenContent never fires
#859 aperta il 10 giu 2026
Metriche repository
- Star
- (1010 star)
- Metriche merge PR
- (Metriche PR in attesa)
Descrizione
What happened?
Description
On iOS 26.5 (physical devices only), after a rewarded full-screen ad is
dismissed non-interactively, the underlying app screen becomes frozen: all
touches are dead, but the app is not crashed and the JS thread is still alive (our
timers keep firing). adDidDismissFullScreenContent (CLOSED) is NEVER emitted.
The freeze survives a background→foreground cycle (app switcher AND going to the
home screen and back). Only a full force-quit of the app restores interactivity.
When the user manually taps the close (X) button, everything works (CLOSED fires, no freeze). The freeze only happens on a non-interactive dismissal.
Not reproducible in the iOS Simulator — physical device only.
Steps to reproduce
Two reliable triggers:
- Auto-dismiss: watch a rewarded ad that closes by itself (end of video / end-card).
- Background during the ad (≈100% reproducible): start a rewarded ad → send the app to the background (home screen) while the ad is showing → reopen the app from its icon → the screen behind is frozen.
Expected
The ad dismisses, CLOSED fires, the underlying screen is interactive.
Actual
The ad disappears visually but the underlying screen is unresponsive to touches; CLOSED
never fires; only force-quitting the app recovers. Happens on any underlying screen (not
tied to a specific navigation).
What we already ruled out (none fixed it)
- Google Mobile Ads iOS SDK 13.5.0 (latest) — same.
- react-native-google-mobile-ads 16.3.3 (latest).
- react-native-screens 4.16 → 4.24 (the iOS 26 "interactions during transition" fix).
- Disabling AdMob ad pods on the rewarded unit (only helped sequential ads).
- Our own foreground-recovery / fresh-reload logic, and the keyWindow / app-switcher trick.
This looks like an iOS 26.5 view-controller / window lifecycle issue triggered when the ad's full-screen VC is dismissed without user interaction (we also filed Apple Feedback).
Environment
- react-native-google-mobile-ads: 16.3.3
- Google Mobile Ads iOS SDK: 13.5.0 (also seen on 13.1.0 / 13.4.0)
- React Native 0.81.5, Expo SDK 54, New Architecture (Fabric) enabled
- iOS 26.5, physical iPhone (seen on iPhone 11/13/15/16/16 Pro/17)
- Ad format: Rewarded, presented from the rootViewController
Extra
Happy to provide a screen recording of the freeze and a device console log showing
show() called with no subsequent reward/dismiss callback. We also have a related Google
AdMob support ticket (7-7376000040875).
Platforms
Only on iOS
React Native Info
System:
OS: macOS 26.5.1
Binaries:
Node: 25.3.0
npm: 11.7.0
Watchman: 2026.04.20.00
Managers:
CocoaPods: 1.16.2
SDKs:
iOS SDK: iOS 26.2 (build) — test devices on iOS 26.5
IDEs:
Xcode: 26.2 / 17C52
npmPackages:
expo: 54.0.33
expo-router: 6.0.23
react: 19.1.0
react-native: 0.81.5
react-native-google-mobile-ads: 16.3.3 (Google Mobile Ads iOS SDK 13.5.0)
Expo Workflow: bare (prebuild)
New Architecture: enabled
Are your using Typescript?
- My project is using Typescript
package.json
{
"name": "noctua",
"version": "1.0.0",
"main": "expo-router/entry",
"scripts": {
"start": "expo start",
"start:tunnel": "expo start --tunnel",
"start:clear": "expo start --clear",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "expo lint"
},
"dependencies": {
"@expo-google-fonts/archivo-black": "^0.4.1",
"@expo-google-fonts/cormorant-garamond": "^0.4.1",
"@expo-google-fonts/crimson-text": "^0.4.0",
"@expo-google-fonts/eb-garamond": "^0.4.2",
"@expo-google-fonts/inter": "^0.4.2",
"@expo-google-fonts/literata": "^0.4.3",
"@expo-google-fonts/lora": "^0.4.2",
"@expo-google-fonts/manrope": "^0.4.2",
"@expo-google-fonts/outfit": "^0.4.3",
"@expo-google-fonts/playfair-display": "^0.4.2",
"@expo-google-fonts/plus-jakarta-sans": "^0.4.2",
"@expo-google-fonts/righteous": "^0.4.1",
"@expo-google-fonts/sora": "^0.4.2",
"@expo-google-fonts/space-grotesk": "^0.4.1",
"@expo-google-fonts/spectral": "^0.4.1",
"@expo-google-fonts/unbounded": "^0.4.1",
"@expo/ngrok": "^4.1.3",
"@expo/vector-icons": "^15.0.3",
"@gorhom/bottom-sheet": "^5.2.8",
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-firebase/analytics": "^23.8.5",
"@react-native-firebase/app": "^23.8.5",
"@react-native-masked-view/masked-view": "^0.3.2",
"@supabase/supabase-js": "^2.91.0",
"expo": "~54.0.33",
"expo-application": "^7.0.8",
"expo-blur": "~15.0.8",
"expo-build-properties": "~1.0.10",
"expo-clipboard": "~8.0.8",
"expo-constants": "~18.0.13",
"expo-device": "~8.0.10",
"expo-font": "^14.0.11",
"expo-glass-effect": "~0.1.9",
"expo-haptics": "~15.0.8",
"expo-linear-gradient": "~15.0.8",
"expo-linking": "~8.0.11",
"expo-network": "~8.0.8",
"expo-notifications": "~0.32.16",
"expo-router": "~6.0.23",
"expo-splash-screen": "^31.0.13",
"expo-status-bar": "~3.0.9",
"expo-store-review": "~9.0.9",
"expo-tracking-transparency": "~6.0.8",
"lucide-react-native": "^0.562.0",
"nativewind": "^4.2.1",
"react": "19.1.0",
"react-native": "0.81.5",
"react-native-context-menu-view": "^1.21.0",
"react-native-gesture-handler": "~2.28.0",
"react-native-google-mobile-ads": "^16.3.3",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "^15.12.1",
"react-native-webview": "^13.15.0",
"react-native-worklets": "^0.5.1",
"react-native-worklets-core": "^1.6.2",
"tailwindcss": "^3.3.2",
"zod": "^4.4.3",
"zustand": "^5.0.10"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/react": "~19.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"babel-preset-expo": "^54.0.10",
"eslint": "^9.0.0",
"eslint-config-expo": "~10.0.0",
"jest": "^29.7.0",
"jest-expo": "~54.0.17",
"sharp": "^0.34.5",
"typescript": "~5.9.2"
},
"private": true
}
app.json
{
"expo": {
"name": "Noctua",
"slug": "noctua",
"version": "1.1.2",
"orientation": "portrait",
"userInterfaceStyle": "dark",
"newArchEnabled": true,
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.noctuaapp.ios",
"infoPlist": {
"ITSAppUsesNonExemptEncryption": false,
"UIViewControllerBasedStatusBarAppearance": false
}
},
"plugins": [
"expo-router",
["expo-notifications", { "sounds": [] }],
["expo-build-properties", {
"ios": { "useFrameworks": "static", "buildReactNativeFromSource": true }
}],
["expo-tracking-transparency", {
"userTrackingPermission": "..."
}],
["react-native-google-mobile-ads", {
"androidAppId": "ca-app-pub-2493346823798924~4168194614",
"iosAppId": "ca-app-pub-2493346823798924~4714590145",
"skAdNetworkItems": ["... 47 standard AdMob SKAdNetwork IDs, omitted ..."]
}],
"@react-native-firebase/app",
"./plugins/withFirebaseDebug.js",
"./plugins/withNavigationBarFix.js",
"./plugins/withGoogleMobileAdsSdkVersion.js"
]
}
}
ios/Podfile
# Expo-generated Podfile. Relevant lines:
$RNGoogleMobileAdsSDKVersion = '13.5.0' # pinned via our config plugin (tested 13.4.0 & 13.5.0)
platform :ios, '15.1'
use_expo_modules!
use_frameworks! :linkage => :static # from expo-build-properties
android/build.gradle
N/A — iOS-only issue (Android is generated by Expo prebuild and unaffected)
android/app/build.gradle
N/A — iOS-only issue (Android is generated by Expo prebuild and unaffected)
android/settings.gradle
N/A — iOS-only issue (Android is generated by Expo prebuild and unaffected)
AndroidManifest.xml
N/A — iOS-only issue (Android is generated by Expo prebuild and unaffected)