Skip to main content

Installation

Installing is easy

npm i -s @op-engineering/op-sqlite
npx pod-install@latest

If you are using Expo, you cannot add this library on a expo-go app, you need to pre-build your app. There are no needed plugins, as long as pod install runs it's all you need:

npx expo install @op-engineering/op-sqlite
npx expo prebuild

This package only runs on iOS, Android and macOS, same as expo-sqlite, this is a technical limitation as sqlite does not run on the browser.

Configuration

SQLite is very customizable on compilation level. op-sqlite also allows you add extensions or even change the base implementation. You can do this by adding the following to your package.json:

{
// ... the rest of your package.json
// All the keys are optional, see the usage below
"op-sqlite": {
"sqlcipher": false
// "crsqlite": false,
// "performanceMode": true,
// "iosSqlite": false,
// "sqliteFlags": "-DSQLITE_DQS=0",
// "fts5": true,
// "rtree": true,
// "libsql": true,
// "sqliteVec": true,
// "tokenizers": ["simple_tokenizer"]
}
}

All keys are optional, only turn on the features you want:

  • sqlcipher allows to change the base sqlite implementation to sqlcipher, which encrypts all the database data with minimal overhead. You will still need to keep your encryption key secure. Read more about security in React Native here.
  • crsqlite is an extension that allows replication to a server backed sqlite database copy. Repo here.
  • performanceMode turns on certain compilation flags that make sqlite speedier at the cost of disabling some features. You should almost always turn this on, but test your app thoroughly.
  • iosSqlite uses the embedded iOS version from sqlite, which saves disk space but may use an older version and cannot load extensions as Apple disables it due to security concerns. On Android SQLite is always compiled from source as each vendor messes with sqlite or uses outdated versions.
  • sqliteFlags allows you to pass your own compilation flags to further disable/enable features and extensions. It follows the C flag format: -D[YOUR_FLAG]=[YOUR_VALUE]. If you are running large queries on large databases sometimes on Android devices you might get a IO exception. You can disable temporary files by using adding the "-DSQLITE_TEMP_STORE=2" flag.
  • fts5 enables the full text search extension.
  • tokenizers allows you to write your own C tokenizers. Read more in the corresponding section in this documentation.
  • rtree enables the rtree extension
  • sqliteVec enables sqlite-vec, an extension for RAG embeddings

Some combination of features are not allowed. For example sqlcipher and iosSqlite since they are fundamentally different sources. In this cases you will get an error while doing a pod install or during the Android build.

Conflicts

use_frameworks

In case you are using use_frameworks (for example because you are using react-native-firebase), this will break the compilation process and force the compilation to use the embedded sqlite on iOS. One possible workaround is putting this in your Podfile:

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

It forces static compilation on op-sqlite only. Since everything is compiled from sources this should work, however do it at your own risk since other compilation errors might arise.

Compilation clashes

If you have other packages that are dependent on sqlite you will have issues. Some of the known offenders are:

  • expo-updates
  • expo-sqlite
  • cozodb
  • Other packages that try to add/link sqlite (or the sqlite3 cocoapod)

Expo Updates

expo-updates now has a added a new way to avoid a hard dependency on sqlite. Adding "expo.updates.useThirdPartySQLitePod": "true" to ios/Podfile.properties.json fixes the duplicate symbols and header definition issues when expo-updates is the only conflicting package.

An expo plugin can also be used:

import type { ConfigPlugin } from '@expo/config-plugins';
import { withPodfileProperties } from '@expo/config-plugins';

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

export default withUseThirdPartySQLitePod;

If you cannot remove the dependency each of the packages will try to compile sqlite from sources or link it on build time. Even if they manage to compile, they might compile sqlite with different compilation flags and you might face runtime errors.

Another workaround for the expo packages, is you can use the iOS embedded version of sqlite for op-sqlite, in your package.json turn on the iosSqlite flag:

"op-sqlite": {
"iosSqlite": true
}

This means however, you will be used whatever version the phone is running, which might be outdated and it also does not support extension loading. There is no way around this.

Libsql and Expo Updates

If you want to use expo-updates and libsql at the same time there is one more workaround you need to apply. On your AppDelegate (or wherever you initialize your RN view if it's a brownfield integration), you need to call [OPSQLite expoUpdatesWorkaround]; before initializing the RN view. In case of a normal expo app modify the AppDelegate.mm as follows:

#import "OPSQLite.h" // Add the header

// Modify the didFinishLaunchingWithOptions function
-(BOOL)application: (UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
self moduleName = @"main";
self.initialProps = 0{};

// Add the call to the workaround
[OPSQLite expoUpdatesWorkaround];

return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

Other

For other conflicts and compilation errors there is no documented solutions. You need to get rid of the double compilation by hand, either by patching the compilation of each package so that it still builds or removing the dependency on the package.

On Android you might be able to get away by just using a pickFirst strategy. On iOS depending on the build system you might be able to patch it via a post-build hook, something like:

pre_install do |installer|
installer.pod_targets.each do |pod|
if pod.name.eql?('expo-updates')
# Modify the configuration of the pod so it doesn't depend on the sqlite pod
end
end
end