Do Push Notifications Work in WebView Apps? (Yes — Here's How)

Do Push Notifications Work in WebView Apps? (Yes — Here's How)

Short answer: yes, push notifications work in WebView apps — and they work exactly the same way as in a fully native app. The confusion comes from assuming a WebView app behaves like a browser tab. It doesn't. Your app's native shell handles everything: token registration, permission prompts, notification display, tap handling. Your website's code is never involved.

This post covers what WebView push actually is, how it works on Android and iOS, the 2025–2026 API changes that can silently break existing integrations, a step-by-step implementation walkthrough, the seven most common silent failure points, and the questions developers actually ask.


What Is a WebView App, and Why Does Push Seem Complicated?

A WebView app is a native Android or iOS shell that displays your website inside an embedded browser component. Push notifications in WebView apps are handled by that native shell — not the web page — which is why standard web push code won't work on its own.

The shell is written in Java/Kotlin (Android) or Swift/Objective-C (iOS). It wraps Android's WebView component or iOS's WKWebView. The shell owns everything that touches the OS: device sensors, camera, file system, permissions — and critically, notifications. The web page inside controls UI, content, and JavaScript logic.

If you want a fuller look at how WebView apps compare to going fully native, see WebView App vs. Native App: Which Is Right for Your Business?.

Web Push vs. Native Push — The Confusion That Sends Developers the Wrong Way

There are two completely separate push systems, and conflating them is the root of most confusion:

  • Web push (PWA) — browser-based, powered by Service Workers. Works in Chrome, Firefox, and Safari 16.4+ inside a browser.
  • Native push (FCM/APNs) — used by all native and WebView apps. FCM (Firebase Cloud Messaging) on Android, APNs (Apple Push Notification service) on iOS.

In a WebView app, you always use native push. Service Workers don't run inside Android WebView or iOS WKWebView — they need a full browser context. Any web push code living on your website is invisible to the app's notification system.

Feature Web Push (PWA) Native Push (WebView App)
Registration mechanism Service Worker in browser Native SDK in app shell
Works on iOS WebView ❌ No ✅ Yes (APNs)
Works on Android WebView ❌ No ✅ Yes (FCM)
Requires native code ✅ (in the shell)
Works when app is closed ⚠️ Limited ✅ Full OS delivery

Founders often assume their website's push notifications will "carry over" into the app. They won't — different system, different registration path, different delivery mechanism.

Native Push Architecture in WebView Apps Diagram showing the flow from Backend API to OS Services (FCM/APNs), to Native App Shell, and finally OS display or WebView injection. Your Backend API FCM / APNs (OS Native Service) Native App Shell (Handles SDK & Data) OS Display WebView UI

Do Push Notifications Actually Work in WebView Apps on Android and iOS?

Yes — fully, on both platforms. The native shell registers with Google's FCM (Android) or Apple's APNs (iOS) to receive a device token, and the OS delivers notifications independently of whatever your website is rendering in the WebView.

On Android, the Java/Kotlin shell integrates the Firebase Messaging SDK. On install or first launch, FCM assigns a unique device token — the address your backend sends notifications to. When a push arrives, FCM routes it through the device OS to the native shell, which displays it on the lock screen, in the notification drawer, and as an app icon badge. The web page inside the WebView is entirely uninvolved.

On iOS, the Swift/Objective-C shell uses Apple's UNUserNotificationCenter and registers with APNs. APNs returns a device token forwarded to your backend (or to FCM's iOS integration). iOS requires explicit user permission before any notification can appear — the WKWebView has zero involvement in delivery or display.

Rich notifications, deep links, and in-app banners all work:

  • Images, action buttons, and custom sounds come from the native notification payload — no website code needed.
  • Tapping a notification triggers the native shell to read the push payload and load a specific URL in the WebView. Your existing URL structure (e.g., https://yourstore.com/orders/12345) is your deep link system — no custom routing needed.
  • When the app is open, the native layer can suppress the system notification and instead call a JavaScript function injected into the WebView: webView.evaluateJavascript("showInAppBanner('...')", null) on Android, webView.evaluateJavaScript("showInAppBanner('...')") on iOS. Your website renders whatever banner it likes, fully styled to match your design.

What Changed in 2025–2026 That WebView App Developers Must Know

The three changes affecting most WebView apps right now: the FCM legacy API is fully shut down, Android 13+ requires a runtime notification permission most apps haven't added, and iOS 18 changed how notification payloads should be structured.

FCM Legacy API — Gone

Google shut down the FCM legacy HTTP API in June 2024. Any backend still calling https://fcm.googleapis.com/fcm/send returns errors and delivers nothing to users. No crash, no log entry — just silence.

Search your codebase for that endpoint. If you find it, you're on the legacy API. The migration path:

  • Replace the legacy endpoint with https://fcm.googleapis.com/v1/projects/{project_id}/messages:send
  • Generate a service account JSON key from Firebase Console
  • Update authentication from Authorization: key=<server_key> to Authorization: Bearer <OAuth token>
  • Update any Firebase Admin SDK to a version released after 2023

If you use OneSignal, Braze, or Firebase Admin SDK v11+, this may already be handled — but verify.

Android 13+ Runtime Permission

Android 13 (API level 33) introduced POST_NOTIFICATIONS as a runtime permission. Users must explicitly grant it — adding it to the manifest alone does nothing. By 2025–2026, over 85% of active Android devices run API 33 or higher. If you haven't implemented the runtime request, the vast majority of your Android users receive nothing, silently.

Add <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> to AndroidManifest.xml, then call ActivityCompat.requestPermissions() at the right moment in the user journey — not on cold launch.

iOS 18 Notification Changes

iOS 18 introduced AI-powered notification summaries: the OS groups notifications by app and can rewrite them. Notifications without clear title and body fields may be summarised in ways that lose context. Background App Refresh limits also tightened for silent/data-only pushes.

Use high-priority FCM messages for time-sensitive notifications; reserve data-only pushes for non-urgent background syncs. Verify the Push Notifications entitlement is present in Xcode after any Xcode version upgrade.

One more iOS note: Apple enabled Safari web push on iOS 16.4 — but that applies to Safari the browser, not WKWebView. In 2026, WKWebView still doesn't support Service Workers or the Web Push API. If someone tells you to "use web push in your WebView app," they're confusing two different things. The APNs native path is not a workaround; it's the correct, more reliable option.

How to Implement Push Notifications in a WebView App (Step by Step)

To add push notifications to a WebView app: set up credentials, add the FCM or APNs SDK to your native shell, request runtime permission at the right moment, send device tokens to your backend, send pushes via the FCM v1 API, and handle notification taps to deep link into the WebView.

For the App Store submission side of releasing your app once push is wired up, see What Is App Store Submission? A Complete Guide.

  1. Set up Firebase (Android) or APNs credentials (iOS)

    Android: Create or open a Firebase project at console.firebase.google.com. Add your app using its package name (e.g., com.yourstore.app). Download google-services.json and place it in your app module's root directory. Enable Cloud Messaging in Firebase Console.

    iOS: In the Apple Developer portal under Certificates, Identifiers & Profiles → Keys, create an APNs key (.p8 file). Note the Key ID and Team ID. Recommended: use FCM for both platforms and upload your .p8 to Firebase Console → Project Settings → Cloud Messaging → iOS App Configuration. One token-management system for both OS versions.

  2. Add the FCM SDK to your Android shell

    In build.gradle, add the Google services plugin at the project level and firebase-messaging at the app level. Create a class extending FirebaseMessagingService and override two methods:

    • onNewToken(token: String) — fires when FCM assigns or refreshes the device token; POST it to your backend immediately every time.
    • onMessageReceived(remoteMessage: RemoteMessage) — fires when a message arrives while the app is in the foreground; build and show a local notification using NotificationCompat.Builder.

    Register the service in AndroidManifest.xml with the MESSAGING_EVENT intent filter.

  3. Add push support to your iOS shell

    In Xcode: select your target → Signing & Capabilities → + Capability → Push Notifications. Add Background Modes → Remote Notifications if you need silent pushes. In AppDelegate.swift:

    • Call UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
    • On grant, call UIApplication.shared.registerForRemoteNotifications()
    • Implement didRegisterForRemoteNotificationsWithDeviceToken — if using Firebase, pass the token via Messaging.messaging().apnsToken = deviceToken
    • Implement didFailToRegisterForRemoteNotificationsWithError for error logging
  4. Request runtime permission at the right moment

    Don't ask on the first cold launch before the user has experienced any value. Show a pre-permission rationale screen first — something like "Enable notifications to track your order and get exclusive offers" with Allow / Maybe Later buttons. Trigger the OS dialog only when the user taps Allow. This one screen typically lifts opt-in rates by 20–40%.

  5. Send device tokens to your backend

    Every time onNewToken (Android) or didRegisterForRemoteNotificationsWithDeviceToken (iOS) fires, POST { user_id, token, platform } to your API. Store tokens in a push_tokens table with user_id, token, platform, created_at, updated_at — one user may have tokens for multiple devices. When FCM returns UNREGISTERED or INVALID_ARGUMENT errors, delete those stale tokens from the database.

  6. Send pushes via the FCM v1 API

    {
      "message": {
        "token": "<device_token>",
        "notification": {
          "title": "Your order has shipped!",
          "body": "Tap to track your delivery."
        },
        "data": {
          "screen": "order_detail",
          "order_id": "12345"
        },
        "android": { "priority": "high" },
        "apns": {
          "payload": { "aps": { "sound": "default" } }
        }
      }
    }

    Authentication is a Bearer token from service account OAuth 2.0 — not a static server key.

  7. Handle notification taps and deep link into the WebView

    App in background or closed: On Android, read notification data from getIntent().extras in your launcher Activity. On iOS, implement userNotificationCenter(_:didReceive:withCompletionHandler:) in AppDelegate. In both cases, read the data payload (screen, order_id, etc.) and call webView.loadUrl(targetUrl) or webView.load(URLRequest(url:)).

    App in foreground: FCM/APNs delivers to onMessageReceived / userNotificationCenter willPresent. Either show the system notification and let the tap handler above deal with it, or suppress it and inject JavaScript into the WebView to show a custom in-app banner instead.

  8. Test end-to-end on physical devices

    FCM and APNs do not deliver to simulators or standard emulators. Use Firebase Console → Cloud Messaging → Send test message, paste the device token, and send. Test in all three states: foreground, background, fully closed. Test on Android 13+, Android 12, iOS 17, and iOS 18. Test specifically on Samsung, Xiaomi, and Oppo — manufacturer battery optimization behaves very differently from stock Android.

Opt-In Rate Impact Calculator

Calculate the potential impact of delaying your permission prompt behind a rationale screen vs. asking on cold launch (assuming typical industry baselines).

Opt-in users (Cold Launch ~35%)

3,500

Opt-in users (Rationale Screen ~65%)

6,500

Should You Build Push Yourself or Use a Service?

For most WebView apps, using FCM directly or a third-party platform is faster and more reliable than building push infrastructure from scratch. The right choice depends on who's sending notifications and how much control you need.

  • FCM directly — free, unlimited messages, full programmatic control. Best for developer-led teams and event-triggered notifications (order shipped, payment received). No built-in marketing dashboard; you manage segments in your own backend.
  • OneSignal — generous free tier (check current limits at onesignal.com). Visual dashboard with audience segmentation, A/B testing, and delivery analytics. Best for e-commerce apps where a marketing team sends promotional pushes without touching code. Important: integrate OneSignal's native Android and iOS SDKs into the shell — not the web SDK inside the WebView, which silently fails to register.
  • Braze / Klaviyo — enterprise pricing, deep integration with Shopify/WooCommerce data. Worth it for complex cart abandonment flows or lifecycle automation. Usually overkill for a new or mid-size app.
  • Managed WebView platform — push setup, FCM/APNs integration, permission handling, token management, and device testing are included in the managed build. Nothing to configure. Best for business owners who want push working out of the box. If you're converting a Shopify store or WooCommerce site to an app, see How to Convert Your Shopify Store to a Mobile App for what the full process looks like.

7 Mistakes That Silently Break Push in WebView Apps

The most common reasons push notifications fail silently in WebView apps: missing the Android 13+ runtime permission, calling the deprecated FCM legacy API, misconfigured APNs credentials, not handling FCM token refresh, asking for permission too early, testing on a simulator, and ignoring manufacturer battery optimization. None of these produce an error. They just deliver nothing.

  1. Missing POST_NOTIFICATIONS runtime permission on Android 13+.
    Notifications are silently dropped on 85%+ of active Android devices. Adding the permission to the manifest isn't enough — you also need the ActivityCompat.requestPermissions() call with a rationale dialog. Verify with ADB or Android Studio App Inspection.
  2. Calling the deprecated FCM legacy API.
    Your backend returns HTTP 404 or 410; users receive nothing. Search your codebase for fcm.googleapis.com/fcm/send. If found, migrate to the v1 endpoint with service account OAuth 2.0 authentication.
  3. Misconfigured APNs on iOS.
    Android push works; iOS devices never receive anything. Fix checklist: verify the .p8 key in Firebase matches your bundle ID exactly (case-sensitive); confirm the Push Notifications capability is in Xcode Signing & Capabilities — not just Info.plist; check whether you're targeting the development or production APNs environment and match it to your build type; re-run on a physical device after any entitlement change.
  4. Not handling FCM token refresh.
    Push works on install, then silently stops for individual users after days or weeks — random, hard to reproduce. FCM issues new tokens periodically. Implement onNewToken and POST the new token to your backend every time it fires. Mark old tokens inactive when FCM returns UNREGISTERED.
  5. Asking for notification permission on the first cold launch.
    Opt-in rates drop below 40%; users dismiss reflexively. Once dismissed, they rarely go back to Settings to re-enable. Add a pre-permission rationale screen, trigger the OS dialog only on explicit user intent, and measure before/after opt-in rates.
  6. Testing on a simulator or emulator.
    The integration appears broken when it may be fine. FCM and APNs require the hardware registration path. Always test on a physical Android device and a physical iPhone.
  7. Ignoring manufacturer battery optimization.
    Push works reliably on Google Pixel but delays 10–30+ minutes — or never arrives — on Xiaomi, Samsung, Oppo, and Huawei. Fixes: send pushes with "priority": "high" in the FCM payload (wakes the device from Doze mode); guide users through whitelisting your app from battery optimization during onboarding (for Xiaomi: Security app → Battery → No restrictions); test specifically on these brands before launch.

Frequently Asked Questions

Can a WebView app send push notifications without a backend server?

Almost. For one-off sends, Firebase Console's manual "Send test message" tool works fine. For automated or triggered notifications, a lightweight serverless function — Firebase Cloud Functions, Vercel, AWS Lambda — handles it without maintaining a full server. For production apps with user-targeted notifications, a proper backend is the right approach.

Do push notifications work when the app is closed or the phone is asleep?

Yes, completely. FCM and APNs maintain a persistent OS-level connection that survives app termination, device sleep, and network drops — messages are queued and delivered when connectivity restores. The only exceptions are a revoked notification permission and aggressive manufacturer battery optimization on certain Android devices.

Can I use OneSignal's JavaScript web SDK inside the WebView to avoid native code?

No — and this is one of the most common misconceptions. OneSignal's web SDK relies on Service Workers, which are not supported inside Android WebView or iOS WKWebView. The web SDK will silently fail to register. You must integrate OneSignal's native Android SDK and native iOS SDK into the shell.

How do I send a push notification to a specific user, not all users?

Link device tokens to users in your backend. When a user logs in, read their current FCM token and POST it to your API with their user ID. Store this in a push_tokens table keyed by user_id. When sending to a specific user (e.g., "Your order shipped"), look up their token(s) and send to those. One user may have tokens for multiple devices — send to all of them.

Is push opt-in rate lower for WebView apps than native apps?

No. The system permission dialog is identical whether the app is WebView-based or fully native, because it's an OS-level prompt. Opt-in rates are determined almost entirely by prompt timing and whether you show a pre-permission rationale screen first. A well-timed request in a WebView app will outperform a poorly-timed one in a native app every time.

My WebView app was built by a third-party developer. How do I check if push is set up correctly?

Three checks: (1) Install the app on a fresh device and look for a notification permission dialog during onboarding or after a meaningful action — if it never appears, the runtime permission request may be missing. (2) In Firebase Console → Cloud Messaging → Send test message, paste the device's FCM token (ask your developer) and send — if the notification appears, the basic integration is working. (3) Confirm your developer has an onNewToken handler that posts refreshed tokens to the backend. If that's missing, push will silently stop working for users after a token refresh.

What to Remember

WebView apps use native push via FCM and APNs — not web push, not Service Workers, not anything in your website's code. Users see no difference from a fully native app.

FCM v1 API is mandatory The legacy API is shut down. If your backend calls the old endpoint, push is broken for everyone.
Android 13+ needs a runtime permission request Missing this silently breaks push for the majority of Android users.
iOS WKWebView doesn't support web push Use the APNs native path — it's more reliable, not a workaround.
Ask for permission after a value action With a rationale screen. Expect a 20–40% opt-in lift over cold-launch prompts.
Handle token refresh Not optional. Skipping it causes silent, hard-to-reproduce failures for individual users over time.
Test on physical devices Including Samsung, Xiaomi, and Oppo — manufacturer battery optimization is nothing like stock Android.