different behavior of build and run

Hello, I created a project through Expo, through the instructions Onesignal is installed in it, the problem is that when I execute expo run:ios everything works correctly and I see pushid in the logs, but if I do expo build:ios the application cannot start and in the mac console I see the error “Invariant Violation: Your JavaScript code tried to access a native module that doesn’t exist in this development client.”
I suppose that during build, for some reason, native modules are not installed, please tell me how this can be fixed.

Hi @wizard31337

TL;DR: Build with EAS Build instead of expo build.

There are two ways to build an Expo app:

  • The “Classic” expo build:ios/expo build:android
  • The new eas build

The classic build system does not support dependencies that require native code )other than whatever is part of the Expo SDK). Since the Onesignal SDK is not part of the Expo SDK, you can’t use expo build to build your app. You will also not be able to use the Expo Go app to develop/test your app (unless you’re careful to avoid calling Onesignal in _DEV_ mode.)

With EAS Build, all of your dependencies are compiled into your app, including any with custom native code, like Onesignal. Also, any parts of the Expo SDK that you have not installed as dependencies will not be built into your app. This is the power of EAS Build. Your app will be smaller than if you built it with expo build, and you can install things that would previously required you to eject.

Instead of using the Expo Go app for development/testing you can build a Custom Dev Client. The onesignal-expo-plugin documentation has a link to the blog post that introduced custom clients. See also the documentation. A custom dev client is basically like a customised version of Expo Go that includes all of and only your dependencies. You can use it like you would Expo Go.

One thing to keep in mind: expo run generates native code (ios and android directories) in your app. At that point you have pretty much got a plain React Native app instead of a “managed” Expo app.

At this point you have a couple of options depending on whether you want to make your own changes to the native projects:

If you want to write your own custom native code

You would proceed like you would for a normal React Native app. You can still build with EAS Build and you can still use OTA Updates and Expo push notifications etc. but you need to manage all of the native code yourself and e.g. if you change the icon in app.json then it will not have any effect on your app. You'll need to change the icon in the native projects like you would for a React Native app. If you use expo prebuild it will probably make the necessary changes, but there's a possibility that it will somehow conflict with your custom changes. If you run expo prebuild --clean it will delete and regenerate the ios and android directories, wiping out your custom changes.

If you do not need to make your own custom changes to the native code under ios and android

You have two options:

Personally, I avoid running expo run and instead build a custom dev client or build using eas build or eas build --local.

If I am writing my own Config Plugin for a package that doesn’t already come with one I use expo prebuild for testing the config plugin, but afterwards I immediately remove the ios and android directories and revert the changes to .gitignore, package.json, yarn.lock etc.