Skip to content

Expo offline support with op-sqlite and react-native-firebase #2926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
andreaslydemann opened this issue Feb 3, 2025 · 5 comments
Closed
Assignees

Comments

@andreaslydemann
Copy link
Contributor

andreaslydemann commented Feb 3, 2025

Describe the bug
When you install react-native-firebase in an expo app, it is stated in the docs that you need to set useFrameworks: 'static' with expo-build-properties for react-native-firebase to work.

Unfortunately that setting doesn't work with the library op-sqlite that is used for offline mode:
https://op-engineering.github.io/op-sqlite/docs/installation#use_frameworks

You would get the following EAS build error from adding op-sqlite while having useFrameworks: 'static' enabled:

Image Image

That basically makes offline mode impossible together with react-native-firebase.

This issue is pretty much a duplicate of the same issue I reported that also existed with the previous sqlite library "react-native-quick-sqlite". I'm a bit surprised that we're dealing with the same issue again after migrating to a new sqlite library:
#2123

To Reproduce
Steps to reproduce the behavior:

  1. Install react-native-firebase and set useFrameworks: 'static' in the app.json file while having op-sqlite installed for offline mode.
  2. Run EAS build.
  3. See EAS build error.

Expected behavior
I expect the app to work with react-native-firebase while having offline support enabled. But that is currently not possible as far as I can see.

@andreaslydemann andreaslydemann changed the title Expo offline support and react-native-firebase Expo offline support with op-sqlite and react-native-firebase Feb 3, 2025
@isekovanic isekovanic self-assigned this Feb 17, 2025
@isekovanic
Copy link
Contributor

isekovanic commented Feb 17, 2025

Hi @andreaslydemann ,

Thanks a lot for the report as well as your patience !

It is indeed not ideal that we have to go through this again with the new library, especially considering that RN Firebase is a very natural and intuitive package to have within a Chat application.

In any case, I managed to play around with this today and have something that is at least working on my end and would love for you to try out whenever you have some time. The domain of the problem lies in exactly what you described as outlined in both the op-sqlite as well as RN Firebase libraries. We essentially need a way to let the pod installation process know that we do not want this particular set of rules (in this case use_frameworks! :linkage => :static) be applied to op-sqlite specifically, as it breaks.

Here's an example of a custom Expo plugin I crafted for this purpose:

// customPlugin.js
const fs = require('fs');
const { withPlugins, createRunOncePlugin, withDangerousMod, IOSConfig } = require('@expo/config-plugins');

const modifyPodfile = (podfilePath) => {
  const preInstallBlock = `
  pre_install do |installer|
    installer.pod_targets.each do |pod|
      if pod.name.eql?('op-sqlite')
        def pod.build_type
          Pod::BuildType.static_library
        end
      end
    end
  end
`;

  if (fs.existsSync(podfilePath)) {
    let podfileContent = fs.readFileSync(podfilePath, 'utf-8');

    // Look for the `use_frameworks!` line
    const useFrameworksLine = "use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']";

    if (podfileContent.includes(useFrameworksLine)) {
      // Insert the pre_install block just before the use_frameworks line
      const modifiedContent = podfileContent.replace(useFrameworksLine, preInstallBlock + '\n' + useFrameworksLine);

      // Write the modified content back to the Podfile
      fs.writeFileSync(podfilePath, modifiedContent);
      console.log('CUSTOM MODIFY PODFILE PLUGIN: Podfile modified to include pre_install block.');
    } else {
      console.log('CUSTOM MODIFY PODFILE PLUGIN: use_frameworks line not found in Podfile.');
    }
  } else {
    console.log('CUSTOM MODIFY PODFILE PLUGIN: Podfile not found.');
  }
}

const withModifyPodfile = config => {
  return withDangerousMod(config, [
    'ios',
    async config => {
      const path = IOSConfig.Paths.getPodfilePath(config.modRequest.projectRoot);
      modifyPodfile(path);
      return config;
    },
  ]);
};

const withStreamOfflineMode = config => {
  return withPlugins(config, [
    withModifyPodfile
  ]);
};

module.exports = createRunOncePlugin(withStreamOfflineMode, 'custom-modify-podfile-plugin', '0.0.1');

which we can then add to our app.json like so:

{
  "expo": {
    // rest of the app.json here
    "plugins": [
     // other plugins here
      "./customPlugin.ts"
    ]
  }
}

It's important to make sure that the plugin is the last in the list, as plugin execution is done in a pipeline fashion and we want all other changes to have been processed before we proceed with ours.

Having this logic as a separate plugin means that it will be executed in the prebuild phase and it should come as a given that all changes should be processed before pod install starts internally and so the updated build rules applied in our generated Podfile during eas build.

It should also work out of the box for local builds (expo run:ios for example) as the same logic applies. Given the logs, the output should look something like this:

✔ Created native directory
✔ Updated package.json | no changes
CUSTOM MODIFY PODFILE PLUGIN: Podfile modified to include pre_install block.
✔ Finished prebuild
✔ Installed CocoaPods
› Skipping dev server
› Planning build
...

Also, please make sure to remove the ios folder entirely from your project if it's an Expo managed one to make sure that everything gets generated from a clean slate.

The example is obviously a bit rough around the edges as it abuses a specific line in my own Podfile to add the preInstallBlock to. I'd like to first see if it indeed does the trick for you as well and if that is indeed the case, we might consider packaging it within the SDK (would take a bit more work since it's not as trivial to properly cover all cases and configurations - it is very likely the case that this is much easier to write on the integrator's side most of the time) and exporting it to be used.

Please do let me know if this indeed gets rid of the issue and allows both libraries to be used simultaneously.

Thanks !

@andreaslydemann
Copy link
Contributor Author

Thanks for the solution @isekovanic, I'll give this a go soon and let you know how it works out!

@isekovanic
Copy link
Contributor

Heya @andreaslydemann , did you manage to try it out ?

@andreaslydemann
Copy link
Contributor Author

andreaslydemann commented Mar 2, 2025

I have had a chance to try it out now, and it fixes the issue I had before but my build failed for a different reason:

typedef redefinition with different types ('struct (unnamed struct at /Users/expo/workingdir/build/apps/client/ios/Pods/Headers/Public/op-sqlite/sqlite3.h:10549:16)' vs 'struct sqlite3_snapshot')

It seems to conflict with the sqlite library used for expo-updates, but I fixed that with the expo plugin in this post:
OP-Engineering/op-sqlite#213

I added the solution to your plugin like so:

const {
  withPlugins,
  createRunOncePlugin,
  withDangerousMod,
  withPodfileProperties,
  IOSConfig,
} = require('@expo/config-plugins');

...

const withUseThirdPartySQLitePod = (expoConfig) => {
  return withPodfileProperties(expoConfig, (config) => {
    config.modResults = {
      ...config.modResults,
      'expo.updates.useThirdPartySQLitePod': 'true',
    };
    return config;
  });
};

const withStreamOfflineMode = (config) => {
  return withPlugins(config, [withModifyPodfile, withUseThirdPartySQLitePod]);
};

Now it's all completely working. Thanks a lot for the help!

@isekovanic
Copy link
Contributor

Ah, of course - that's my bad, I forgot to take expo-updates into account. Nice catch !

That's awesome by the way, glad it does the trick. Tomorrow I'll update the docs with this likely in the troubleshooting guide(s) and we'll start from there, later on I'll see if this can be integrated into our SDK in a more elegant way for users of the SDK to just use freely.

With that said, I'm closing the ticket; thanks for all your feedback and testing ! It's much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants