← Bloga dön

OneSignal Expo: Production Push Setup and Common Errors

Getting OneSignal Expo push live is less about pasting an App ID and more about three realities: native modules (not Expo Go), platform credentials (APNs + FCM), and permission timing on modern iOS and Android. This how-to + troubleshooting guide walks React Native / Expo teams through a full production setup—plugin config, SDK init, tokens and External IDs—then the error strings that burn the most hours in QA.

Soft note for kit users: In the CodeBaseHub Expo starter kit (Expo ~57 / RN 0.86), OneSignal is integrated and on by default (oneSignal: true in features.ts) once you set EXPO_PUBLIC_ONESIGNAL_APP_ID. That is push-ready wiring—not zero setup. You still own the OneSignal dashboard, store credentials, a real device build, and any deep-link routing after a tap. See OneSignal docs and feature flags.

Key Takeaways

  • OneSignal needs a development / EAS build. Official docs: push does not work in Expo Go because the SDK depends on native modules (Expo SDK setup).
  • Put onesignal-expo-plugin first in the Expo plugins array to avoid OneSignal/OneSignal.h file not found.
  • Match mode / aps-environment to the build type (development vs production / TestFlight / App Store).
  • Prefer a value-first permission prompt; iOS always required opt-in, and Android 13+ requires runtime POST_NOTIFICATIONS (Android Developers).
  • After auth, bind External ID (e.g. Firebase UID) so one person maps across devices.
  • Deep links are not automatic: Additional Data + a click listener give payload; you still implement navigation. Do not treat Airbridge as "deep links work out of the box."

The flow: Expo Go will not work for native push; you need a development build for OneSignal. Production builds require matching development vs production mode across your plugin configuration and Apple entitlements.

Why OneSignal on Expo

Expo's expo-notifications can obtain tokens and schedule local alerts. Expo is clear that remote push via that library is unavailable in Expo Go on Android from SDK 53—you need a development build (Expo Notifications).

Teams choose OneSignal Expo push when they need more than a raw device token pipeline:

  1. A dashboard + REST API for segments, journeys, and test cohorts without standing up your own campaign UI
  2. Native SDK helpers for permission state, subscription observers, and optional in-app messages
  3. User-level identity (External ID) that survives reinstalls better than " whichever device token we saw last"

You can still use Expo's APIs for local reminders. For remote production messaging, pick a primary provider and keep credentials, prompts, and metrics in one place.

Permission still has to be earned. Airship's 2026 benchmarks note that after Android 13+, iOS and Android opt-in rates sit near parity—the old Android "on by default" era is over (Airship 2026 push benchmarks). Secondary summaries of OneSignal's 2026 industry data put Software opt-in around ~38% iOS / ~39% Android (Apptrove / OneSignal 2026 table)—directional, not your KPI floor.

This is stack how-to, not a boilerplate ranking. For scaffolding context only, see what an Expo boilerplate includes.

Pick one primary remote push provider per environment. Mixing Expo Push Service and OneSignal without a clear plan duplicates prompts and splits metrics.

Prerequisites

OneSignal app + credentials

Configure iOS (p8 preferred or p12) and Android (FCM) in the OneSignal dashboard. Copy the App ID from Settings → Keys & IDs.

Development or EAS build

OneSignal's Expo guide assumes a managed Expo app plus EAS / development build (Expo SDK setup). Install expo-dev-client, build, and validate remote push on a physical device when possible.

Matching identifiers

ios.bundleIdentifier and android.package must match Apple, Google, and OneSignal. Mismatches often look like "silent failure," not a loud JS exception—the SDK initializes, the permission prompt may even succeed, and the dashboard stays empty or stuck on Never Subscribed.

If you ship Firebase Auth (Firebase Auth Expo), plan Firebase UID → OneSignal External ID after login. Device subscriptions alone are enough for "send to this phone"; External IDs are what make "send to this customer on every device" tractable for support and lifecycle campaigns.

Your setup checklist: OneSignal App ID, APNs p8 key or certificate, FCM server key, an EAS build profile configured, and a physical device for testing end-to-end delivery.

Expo Go vs Development Build (Primary Footgun)

Answer first: Native OneSignal push does not run in Expo Go. Use npx expo run:ios / run:android, a custom dev client, or EAS Build.

OneSignal states it outright: "Push notifications do not work in Expo Go." (Expo SDK setup). The classic symptom:

Could not load RNOneSignal native module. Make sure native dependencies are properly linked.

Maintainers treat that as "you're in Expo Go / missing native binary," not a yarn-link mystery (plugin issue #143). Prebuild + a client that embeds the config plugin fixes it; Expo Go channels do not.

Same lab rule as IAP: UI in Go, native SDKs in a dev build—see also RevenueCat paywalls on Expo.

If push "works in the tutorial screenshot" but throws on npx expo start, check whether you opened Expo Go instead of your development client.

Install and Configure onesignal-expo-plugin

npx expo install onesignal-expo-plugin
npm install --save react-native-onesignal

Required Expo config pieces (docs):

  • Bundle ID / package aligned with stores + OneSignal
  • iOS UIBackgroundModes: ["remote-notification"]
  • aps-environment + plugin mode: development or production
  • onesignal-expo-plugin as the first plugins entry
{
  "expo": {
    "ios": {
      "bundleIdentifier": "com.yourcompany.yourapp",
      "infoPlist": { "UIBackgroundModes": ["remote-notification"] },
      "entitlements": { "aps-environment": "development" }
    },
    "android": { "package": "com.yourcompany.yourapp" },
    "plugins": [
      ["onesignal-expo-plugin", { "mode": "development" }]
    ]
  }
}

For TestFlight / App Store, switch mode and aps-environment to production and rebuild. Leaving development entitlements in a store build is a top "works on my phone, silent in TestFlight" failure.

Kit path: Plugin is already in app.json. Set EXPO_PUBLIC_ONESIGNAL_APP_ID, keep oneSignal: true, then after App ID / native-facing changes run npm run prebuild and rebuild the client.

Your app.json should list onesignal-expo-plugin as the first item in the plugins array to ensure header files are found during the iOS build.

Initialize, Permission, External ID

Use current v5-style APIs (OneSignal.initialize), not older setAppId snippets. Bootstrap in App.tsx or Expo Router app/_layout.tsx:

import { useEffect } from "react";
import { OneSignal, LogLevel } from "react-native-onesignal";

export function useOneSignalBootstrap(appId: string) {
  useEffect(() => {
    OneSignal.Debug.setLogLevel(LogLevel.Verbose); // lower in production
    OneSignal.initialize(appId);
    // Prefer an in-app explainer before this in production UX
    OneSignal.Notifications.requestPermission(false);

    const onClick = (event: unknown) => {
      console.log("OneSignal notification clicked", event);
    };
    OneSignal.Notifications.addEventListener("click", onClick);
    return () => {
      OneSignal.Notifications.removeEventListener("click", onClick);
    };
  }, [appId]);
}

Kit mapping: Init in src/lib/onesignal.ts; first-launch permission from src/app/index.tsx; after Firebase login, External ID = Firebase UID.

Cold-start requestPermission is fine for debugging. For production, explain value first—OneSignal recommends that pattern in the same setup guide. On Android 13+, POST_NOTIFICATIONS is a real runtime permission (Android docs); a poorly timed deny is expensive and often sticky until the user digs through system Settings.

import { OneSignal } from "react-native-onesignal";

export function bindOneSignalUser(firebaseUid: string) {
  OneSignal.login(firebaseUid);
}

Need consent gating? Call OneSignal.setConsentRequired(true) before initialize, then setConsentGiven(true) after opt-in.

Debug with Verbose logs until the first successful Subscribed device, then dial logging back for release builds.

Verify Subscriptions and Delivery

  1. Launch on a test device and accept the prompt.
  2. OneSignal → Audience → Subscriptions — status should become Subscribed (testing section). Before accept you may briefly see Never Subscribed; that is expected.
  3. Add Test Users, create a matching segment named consistently (OneSignal's guides often use Test Users), then send a dashboard or API test.

Validate in order: native registration → OneSignal subscription → APNs/FCM delivery. If step 2 never flips to Subscribed, fix permission and native build before blaming payload JSON. Android emulators need Google Play Services; iOS Simulator is a weak APNs lab—prefer a physical device for the first green end-to-end test.

Answer first: OneSignal can send a Launch URL or Additional Data. Navigation is app-owned. Universal Links / App Links also need Associated Domains + apple-app-site-association (iOS) or Digital Asset Links (Android) before https:// opens the app (OneSignal links).

Additional Data does not navigate by itself—your click listener reads it and calls Expo Router (or React Navigation):

OneSignal.Notifications.addEventListener("click", (event) => {
  const data = event.notification.additionalData as
    | { screen?: string; id?: string }
    | undefined;
  if (data?.screen) {
    // router.push(`/${data.screen}/${data.id}`)
  }
});

Claim boundary: CodeBaseHub defaults airbridge: true with OneSignal, but that is not "deep links / deferred deep links work out of the box." Do not claim production-ready deep-link routing, Remote Config, App Check, or Crashlytics.

Deep link flow: notification is tapped, the click listener fires with additionalData, your app code reads that payload and calls Expo Router or your navigation library to show the correct screen—the routing logic is yours.

Common Errors (Real Strings)

Reproduce on a dev/production build, not Expo Go.

Could not load RNOneSignal native module...

Cause: Expo Go or client built before the plugin. Fix: Prebuild (npm run prebuild in the kit), then expo run:* / EAS (issue #143).

OneSignal/OneSignal.h file not found

Cause: Plugin not first in plugins. Fix: Move onesignal-expo-plugin to index 0, clean prebuild, rebuild (troubleshooting table).

No pushes on iOS

APNs key valid; bundle ID match; aps-environment / mode match development vs production; permission granted; physical device. TestFlight needs production.

No pushes on Android

FCM in OneSignal; Play Services present; Android 13+ permission granted.

Bundle identifier mismatch

Align Expo config, Apple App ID, and OneSignal iOS platform—rebuild.

Cycle Inside... building could produce unreliable results

In Xcode, move Embed Foundation/App Extensions above Run Script (OneSignal iOS notes).

PBXGroup / PBXFileSystemSynchronizedRootGroup

Convert the Notification Service Extension folder to a Group in Xcode.

Subscribed but no rich media / confirmed receipt

Do not set disableNSE: true unless you only want basic pushes—the NSE supports badges, confirmed delivery, and attachments.

Debug order — native build? → permission? → Subscribed? → APNs/FCM match? → only then payload / routing code.

Soft Kit Path (Flags Without Overclaim)

Flag / areaDefaultMeaning
oneSignalONPush wiring when App ID is set
airbridgeONNot the same as finished in-app routes
RevenueCat / Mixpanel / AdMob / email verificationOFFDo not imply enabled
Deep-link routing / Remote Config / App Check / Crashlytics—Not claimed ready

Steps: set EXPO_PUBLIC_ONESIGNAL_APP_ID, confirm oneSignal: true, npm run prebuild after App ID changes, rebuild, test on device. Init src/lib/onesignal.ts; permission src/app/index.tsx; External ID from Firebase UID post-login. Docs: onesignal, feature-flags.

Paywalls are separate—when you enable that later, use RevenueCat on Expo.

FAQ

Does OneSignal work in Expo Go?

No for native push. Use a development build or EAS binary (OneSignal Expo docs).

Do I need both the plugin and react-native-onesignal?

Yes—plugin for prebuild/native config, package for runtime.

Development or production mode?

Development for local/dev-client; production for TestFlight and App Store.

Is an Expo Push Token a OneSignal subscription?

No. Different services and registration paths.

No. You get click payloads / optional Launch URLs; Universal Links and routers are yours (links docs).

Conclusion

Production onesignal expo push is a checklist: native build, plugin-first config, credential parity, permission UX, subscription verification, then identity. Most "SDK broken" threads are Expo Go, wrong aps-environment, or missing FCM/APNs—not mysterious React Native bugs.

Wire the click listener and External ID early, keep deep-link claims honest (payload ≠ router), and test on hardware before you trust a release train. When something fails, walk the debug order in the errors section instead of reinstalling packages at random.

If you want that wiring behind a feature flag on an Expo ~57 stack—OneSignal on by default once the App ID is set—start from the Expo starter kit, then finish credentials and device QA yourself. Soft path, not zero setup.

References

  1. OneSignal — Expo SDK setup
  2. OneSignal — URLs, links & deep links
  3. Expo — Notifications
  4. Android — POST_NOTIFICATIONS
  5. Airship — 2026 push benchmarks
  6. Apptrove — opt-in benchmarks (OneSignal 2026 industry table)
  7. onesignal-expo-plugin #143 — Expo Go / RNOneSignal
  8. CodeBaseHub — OneSignal
  9. CodeBaseHub — Feature flags

İlgili yazılar