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 extensionsqliteVec
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