From 537737bc99a7479c6523ebf0ecc275d892673af6 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 22 Oct 2026 19:34:04 +0100 Subject: [PATCH] refactor: replace NitroSQLiteOnLoad with a platform context A PlatformContext hybrid object now returns the database, temporary and legacy database directories. Kotlin reads them from the Android application context, and Objective-C++ applies the iOS Info.plist options that OnLoad.mm handled at launch. JavaScript passes them to the NitroSQLite constructor, so each root resolves databases against its own directories instead of static paths, and the Android JNI setter and the iOS +load hook go away. Co-Authored-By: Claude Opus 5.5 --- .../react-native-nitro-sqlite/Package.swift | 4 +- .../android/RNNitroSQLiteAdapter.cpp | 38 ------- .../rnnitrosqlite/HybridNitroSQLiteOnLoad.kt | 20 ---- .../rnnitrosqlite/HybridPlatformContext.kt | 25 ++++ .../margelo/rnnitrosqlite/DocPathSetter.kt | 14 --- .../cpp/hybridObjects/HybridNitroSQLite.cpp | 52 ++++++--- .../cpp/hybridObjects/HybridNitroSQLite.hpp | 24 ++-- .../hybridObjects/HybridPlatformContext.hpp | 26 +++++ .../ios/HybridPlatformContext.mm | 107 ++++++++++++++++++ .../react-native-nitro-sqlite/ios/OnLoad.mm | 99 ---------------- .../android/RNNitroSQLite+autolinking.cmake | 2 +- .../generated/android/RNNitroSQLiteOnLoad.cpp | 6 +- .../c++/HybridNitroSQLiteSpecClass.cpp | 6 +- .../c++/JHybridNitroSQLiteOnLoadSpec.cpp | 65 ----------- .../c++/JHybridPlatformContextSpec.cpp | 84 ++++++++++++++ ...pec.hpp => JHybridPlatformContextSpec.hpp} | 32 +++--- ...=> JHybridPlatformContextSpecJavaPart.hpp} | 12 +- ...adSpec.kt => HybridPlatformContextSpec.kt} | 51 ++++++--- .../nitrogen/generated/hybrid-classes.json | 11 +- .../ios/RNNitroSQLiteAutolinking.cpp | 2 + .../ios/c++/HybridNitroSQLiteSpecClass.cpp | 6 +- .../ios/c++/HybridPlatformContextSpec.cpp | 14 +++ .../c++/HybridPlatformContextSpecClass.cpp | 41 +++++++ .../HybridPlatformContextSpec.hpp | 61 ++++++++++ ...roSQLiteOnLoad.cjs => PlatformContext.cjs} | 6 +- ...LiteOnLoad.d.cts => PlatformContext.d.cts} | 4 +- .../src/OnLoad.android.ts | 3 - .../react-native-nitro-sqlite/src/OnLoad.ts | 2 - .../src/__tests__/entrypoints.test.ts | 30 ++--- .../react-native-nitro-sqlite/src/index.ts | 3 - .../react-native-nitro-sqlite/src/nitro.ts | 7 +- .../src/specs/NitroSQLite.nitro.ts | 16 ++- .../src/specs/NitroSQLiteOnLoad.nitro.ts | 13 --- .../src/specs/PlatformContext.nitro.ts | 31 +++++ 34 files changed, 565 insertions(+), 352 deletions(-) delete mode 100644 packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoad.kt create mode 100644 packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContext.kt delete mode 100644 packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/rnnitrosqlite/DocPathSetter.kt create mode 100644 packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridPlatformContext.hpp create mode 100644 packages/react-native-nitro-sqlite/ios/HybridPlatformContext.mm delete mode 100644 packages/react-native-nitro-sqlite/ios/OnLoad.mm delete mode 100644 packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.cpp create mode 100644 packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridPlatformContextSpec.cpp rename packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/{JHybridNitroSQLiteOnLoadSpec.hpp => JHybridPlatformContextSpec.hpp} (56%) rename packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/{JHybridNitroSQLiteOnLoadSpecJavaPart.hpp => JHybridPlatformContextSpecJavaPart.hpp} (52%) rename packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/{HybridNitroSQLiteOnLoadSpec.kt => HybridPlatformContextSpec.kt} (50%) create mode 100644 packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpec.cpp create mode 100644 packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpecClass.cpp create mode 100644 packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/include/RNNitroSQLite/HybridPlatformContextSpec.hpp rename packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/{NitroSQLiteOnLoad.cjs => PlatformContext.cjs} (70%) rename packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/{NitroSQLiteOnLoad.d.cts => PlatformContext.d.cts} (59%) delete mode 100644 packages/react-native-nitro-sqlite/src/OnLoad.android.ts delete mode 100644 packages/react-native-nitro-sqlite/src/OnLoad.ts delete mode 100644 packages/react-native-nitro-sqlite/src/specs/NitroSQLiteOnLoad.nitro.ts create mode 100644 packages/react-native-nitro-sqlite/src/specs/PlatformContext.nitro.ts diff --git a/packages/react-native-nitro-sqlite/Package.swift b/packages/react-native-nitro-sqlite/Package.swift index a826b10c..fdb03336 100644 --- a/packages/react-native-nitro-sqlite/Package.swift +++ b/packages/react-native-nitro-sqlite/Package.swift @@ -14,9 +14,9 @@ let sqlite = SQLiteBuildOptions.resolve() addNitrogenTargets( to: package, - // SwiftPM cannot build Objective-C++ in a Swift target, so OnLoad.mm builds with the C++ sources. + // SwiftPM cannot build Objective-C++ in a Swift target, so HybridPlatformContext.mm builds with the C++ sources. swiftSources: [], - cxxSources: ["cpp", "ios/OnLoad.mm"], + cxxSources: ["cpp", "ios/HybridPlatformContext.mm"], cxxSettings: sqlite.defines.map { .define($0.name, to: $0.value) } ) diff --git a/packages/react-native-nitro-sqlite/android/RNNitroSQLiteAdapter.cpp b/packages/react-native-nitro-sqlite/android/RNNitroSQLiteAdapter.cpp index ec89e9b6..b652bb14 100644 --- a/packages/react-native-nitro-sqlite/android/RNNitroSQLiteAdapter.cpp +++ b/packages/react-native-nitro-sqlite/android/RNNitroSQLiteAdapter.cpp @@ -1,45 +1,7 @@ -#include "HybridNitroSQLite.hpp" #include -#include #include #include -#include -#include -#include - -using namespace margelo::nitro::rnnitrosqlite; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { return facebook::jni::initialize(vm, [] { margelo::nitro::rnnitrosqlite::registerAllNatives(); }); } - -extern "C" JNIEXPORT void JNICALL Java_com_margelo_rnnitrosqlite_DocPathSetter_setDocPathInJNI(JNIEnv* env, jclass clazz, jstring doc_path, - jstring cache_path) { - const char* docPath = env->GetStringUTFChars(doc_path, nullptr); - if (docPath == nullptr) { - return; - } - - const char* cachePath = env->GetStringUTFChars(cache_path, nullptr); - if (cachePath == nullptr) { - env->ReleaseStringUTFChars(doc_path, docPath); - return; - } - - // SQLite caches this environment value during VFS initialization. Do not replace it on JS reload. - static std::once_flag tempDirectoryOnce; - static bool tempDirectoryConfigured = false; - std::call_once(tempDirectoryOnce, [&] { tempDirectoryConfigured = setenv("SQLITE_TMPDIR", cachePath, 1) == 0; }); - - if (tempDirectoryConfigured) { - HybridNitroSQLite::docPath = std::string(docPath); - } else { - jclass exception = env->FindClass("java/lang/IllegalStateException"); - if (exception != nullptr) { - env->ThrowNew(exception, "Could not configure SQLite temporary directory"); - } - } - - env->ReleaseStringUTFChars(cache_path, cachePath); - env->ReleaseStringUTFChars(doc_path, docPath); -} diff --git a/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoad.kt b/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoad.kt deleted file mode 100644 index 58b3336a..00000000 --- a/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoad.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.margelo.nitro.rnnitrosqlite - -import androidx.annotation.Keep -import com.facebook.proguard.annotations.DoNotStrip -import com.margelo.nitro.NitroModules -import com.margelo.rnnitrosqlite.DocPathSetter - -@Keep -@DoNotStrip -class HybridNitroSQLiteOnLoad : HybridNitroSQLiteOnLoadSpec() { - companion object : HybridNitroSQLiteOnLoadSpec.Companion { - @Keep - @DoNotStrip - override fun init() { - NitroModules.applicationContext?.let { context -> - DocPathSetter.setDocPath(context) - } - } - } -} diff --git a/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContext.kt b/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContext.kt new file mode 100644 index 00000000..22bd859d --- /dev/null +++ b/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContext.kt @@ -0,0 +1,25 @@ +package com.margelo.nitro.rnnitrosqlite + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.NitroModules + +/** Android directories for NitroSQLite, read from the application context. */ +@Keep +@DoNotStrip +class HybridPlatformContext : HybridPlatformContextSpec() { + companion object : HybridPlatformContextSpec.Companion { + override val databaseDirectory: String + get() = context.filesDir.absolutePath + + override val temporaryDirectory: String? + get() = context.cacheDir.absolutePath + + override val legacyDatabaseDirectory: String? + get() = null + + private val context + get() = NitroModules.applicationContext + ?: throw IllegalStateException("NitroSQLite needs the React Native application context to find its database directory") + } +} diff --git a/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/rnnitrosqlite/DocPathSetter.kt b/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/rnnitrosqlite/DocPathSetter.kt deleted file mode 100644 index 399ba1f4..00000000 --- a/packages/react-native-nitro-sqlite/android/src/main/kotlin/com/margelo/rnnitrosqlite/DocPathSetter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.margelo.rnnitrosqlite - -import com.facebook.react.bridge.ReactApplicationContext - -object DocPathSetter { - @JvmStatic - fun setDocPath(context: ReactApplicationContext) { - val path = context.filesDir.absolutePath - val cachePath = context.cacheDir.absolutePath - setDocPathInJNI(path, cachePath) - } - - private external fun setDocPathInJNI(docPath: String, cachePath: String) -} diff --git a/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.cpp b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.cpp index f4a316c2..ae537aad 100644 --- a/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.cpp +++ b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.cpp @@ -7,11 +7,14 @@ #include "../NitroSQLiteMacros.hpp" #include "../NitroSQLiteOperations.hpp" #include "HybridPreparedStatement.hpp" +#include #include #include #include #include +#include #include +#include #include #include #include @@ -19,6 +22,24 @@ namespace margelo::nitro::rnnitrosqlite { +// SQLite caches SQLITE_TMPDIR when it initializes its VFS, so only the first root sets it. +static void configureTemporaryDirectory(const std::string& temporaryDirectory) { + static std::once_flag configureOnce; + static bool configured = false; + std::call_once(configureOnce, [&] { configured = setenv("SQLITE_TMPDIR", temporaryDirectory.c_str(), 1) == 0; }); + if (!configured) { + throw std::runtime_error("Could not configure SQLite temporary directory"); + } +} + +HybridNitroSQLite::HybridNitroSQLite(std::string databaseDirectory, const std::optional& temporaryDirectory, + const std::optional& legacyDatabaseDirectory) + : _databaseDirectory(std::move(databaseDirectory)), _legacyDatabaseDirectory(legacyDatabaseDirectory.value_or("")) { + if (temporaryDirectory) { + configureTemporaryDirectory(*temporaryDirectory); + } +} + // Copy any JS-backed ArrayBuffers on the JS thread so they can be safely // accessed from the connection's background worker. static std::optional copyArrayBufferParamsForBackground(const std::optional& params) { @@ -90,11 +111,11 @@ static Promise enqueueConnectionOperation(const SQLiteConnectionPtr& con return promise; } -const std::string getDocPath(const std::optional& location) { +const std::string getDocPath(const std::string& databaseDirectory, const std::optional& location) { if (location && location->find('\0') != std::string::npos) { throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, "Database location contains a NUL byte"); } - std::string tempDocPath = std::string(HybridNitroSQLite::docPath); + std::string tempDocPath = databaseDirectory; if (location) { tempDocPath = tempDocPath + "/" + *location; } @@ -102,8 +123,8 @@ const std::string getDocPath(const std::optional& location) { return tempDocPath; } -const std::string getOldDocPath(const std::optional& location) { - std::string oldDocPath = HybridNitroSQLite::migrationDocPath; +const std::string getOldDocPath(const std::string& legacyDatabaseDirectory, const std::optional& location) { + std::string oldDocPath = legacyDatabaseDirectory; if (location) { oldDocPath = oldDocPath + "/" + *location; } @@ -111,13 +132,14 @@ const std::string getOldDocPath(const std::optional& location) { return oldDocPath; } -const std::string getMigratedDocPath(DatabaseConnections& connections, const std::string& dbName, +const std::string getMigratedDocPath(DatabaseConnections& connections, const std::string& databaseDirectory, + const std::string& legacyDatabaseDirectory, const std::string& dbName, const std::optional& location, bool readOnly = false) { - const auto currentDocPath = getDocPath(location); - if (HybridNitroSQLite::migrationDocPath.empty()) { + const auto currentDocPath = getDocPath(databaseDirectory, location); + if (legacyDatabaseDirectory.empty()) { return currentDocPath; } - const auto oldDocPath = getOldDocPath(location); + const auto oldDocPath = getOldDocPath(legacyDatabaseDirectory, location); std::string selectedPath; connections.withConnectionsLocked([&]() { const auto oldPath = std::filesystem::path(oldDocPath) / dbName; @@ -140,7 +162,8 @@ void HybridNitroSQLite::open(const std::string& dbName, const std::optional& connectionId) { validateDatabaseName(dbName); std::lock_guard lock(_connections.lifecycleMutex); - const auto currentDocPath = getDocPath(location); - if (migrationDocPath.empty()) { + const auto currentDocPath = getDocPath(_databaseDirectory, location); + if (_legacyDatabaseDirectory.empty()) { sqliteRemoveDb(_connections, dbName, currentDocPath, connectionId); return; } - const auto oldDocPath = getOldDocPath(location); + const auto oldDocPath = getOldDocPath(_legacyDatabaseDirectory, location); const auto preferredPath = _connections.physicalPathForKey(connectionId.value_or(dbName)); const auto currentPath = std::filesystem::path(currentDocPath) / dbName; std::error_code ec; @@ -199,7 +223,7 @@ void HybridNitroSQLite::attach(const std::string& mainDbName, const std::string& if (_connections.get(mainDbName)->readOnly) { throw NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase, "Cannot attach a database to a read-only connection"); } - const auto attachedDocPath = getMigratedDocPath(_connections, dbNameToAttach, location); + const auto attachedDocPath = getMigratedDocPath(_connections, _databaseDirectory, _legacyDatabaseDirectory, dbNameToAttach, location); sqliteAttachDb(_connections.get(mainDbName), attachedDocPath, dbNameToAttach, alias); }; diff --git a/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.hpp b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.hpp index 236429a5..1dbbdcb5 100644 --- a/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.hpp +++ b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridNitroSQLite.hpp @@ -14,17 +14,22 @@ namespace margelo::nitro::rnnitrosqlite { */ class HybridNitroSQLite : public HybridNitroSQLiteSpec { public: - HybridNitroSQLite() = default; - -public: - static std::string docPath; - // Directory databases were stored in by previous app versions, when the platform layer has - // relocated docPath (e.g. iOS with RNNitroSQLite_DatabaseLocation set to "ApplicationSupport"). - // When non-empty, databases found there are resolved as they are opened, attached, or dropped. - static std::string migrationDocPath; + /** Create a root that stores its databases in @p databaseDirectory. + * @param temporaryDirectory SQLite's temporary directory. SQLite reads it once per process, + * so the first root that passes one sets it. + * @param legacyDatabaseDirectory Directory that previous app versions stored databases in, + * for example Documents when iOS moves them to Application Support. Databases found there + * are moved as they are opened, attached, or dropped. + * @throws std::runtime_error If SQLite's temporary directory can't be configured. + */ + HybridNitroSQLite(std::string databaseDirectory, const std::optional& temporaryDirectory, + const std::optional& legacyDatabaseDirectory); private: DatabaseConnections _connections; + std::string _databaseDirectory; + // Empty when databases have never been stored anywhere else. + std::string _legacyDatabaseDirectory; public: // Methods @@ -99,7 +104,4 @@ class HybridNitroSQLite : public HybridNitroSQLiteSpec { Promise loadFileAsync(const std::string& dbName, const std::string& location) override; }; -inline std::string HybridNitroSQLite::docPath = ""; -inline std::string HybridNitroSQLite::migrationDocPath = ""; - } // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridPlatformContext.hpp b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridPlatformContext.hpp new file mode 100644 index 00000000..7760707a --- /dev/null +++ b/packages/react-native-nitro-sqlite/cpp/hybridObjects/HybridPlatformContext.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace margelo::nitro::rnnitrosqlite { + +/** Apple platform directories for NitroSQLite, implemented in ios/HybridPlatformContext.mm. + * PlatformContext only has static members, so this class doesn't derive from its generated spec. + * Android implements PlatformContext in Kotlin instead. + */ +class HybridPlatformContext { +public: + /** Database directory selected by the RNNitroSQLite_AppGroup and RNNitroSQLite_DatabaseLocation Info.plist keys. + * @throws std::runtime_error If the app group container doesn't exist or the directory can't be created. + */ + static std::string getDatabaseDirectory(); + + /** Apple platforms keep SQLite's default temporary directory, so this is always empty. */ + static std::optional getTemporaryDirectory(); + + /** The Documents directory when RNNitroSQLite_DatabaseLocation moves databases to Application Support. */ + static std::optional getLegacyDatabaseDirectory(); +}; + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/ios/HybridPlatformContext.mm b/packages/react-native-nitro-sqlite/ios/HybridPlatformContext.mm new file mode 100644 index 00000000..824450ba --- /dev/null +++ b/packages/react-native-nitro-sqlite/ios/HybridPlatformContext.mm @@ -0,0 +1,107 @@ +#include "hybridObjects/HybridPlatformContext.hpp" +#import +#import +#include + +namespace margelo::nitro::rnnitrosqlite { + +namespace { + + [[noreturn]] void throwDirectoryError(NSString* message) { + throw std::runtime_error(message.UTF8String); + } + + NSString* appGroupIdentifier() { + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RNNitroSQLite_AppGroup"]; + } + + NSString* searchPathDirectory(NSSearchPathDirectory directory) { + return NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES).firstObject; + } + +#if TARGET_OS_OSX + NSString* macOSDatabaseDirectory() { + NSFileManager* fileManager = [NSFileManager defaultManager]; + NSError* error = nil; + NSURL* applicationSupportURL = [fileManager URLForDirectory:NSApplicationSupportDirectory + inDomain:NSUserDomainMask + appropriateForURL:nil + create:YES + error:&error]; + if (applicationSupportURL == nil) { + throwDirectoryError([NSString stringWithFormat:@"Could not find the Application Support directory: %@", error]); + } + + NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier] ?: [[NSProcessInfo processInfo] processName]; + NSURL* databaseURL = [applicationSupportURL URLByAppendingPathComponent:bundleIdentifier isDirectory:YES]; + if (![fileManager createDirectoryAtURL:databaseURL withIntermediateDirectories:YES attributes:nil error:&error]) { + throwDirectoryError([NSString stringWithFormat:@"Could not create the database directory: %@", error]); + } + return databaseURL.path; + } +#else + NSString* databaseLocation() { + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RNNitroSQLite_DatabaseLocation"]; + } +#endif + +} // namespace + +std::string HybridPlatformContext::getDatabaseDirectory() { + @autoreleasepool { + NSString* appGroupID = appGroupIdentifier(); + if (appGroupID != nil) { + NSURL* storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupID]; + if (storeURL == nil) { + NSLog(@"Invalid App Group ID provided (%@). Check the value of \"RNNitroSQLite_AppGroup\" in your Info.plist file", appGroupID); + throwDirectoryError(@"Error while initializing SQLite database (AppGroup)"); + } + NSLog(@"Configured with AppGroup ID: %@", appGroupID); + return storeURL.path.UTF8String; + } + +#if TARGET_OS_OSX + return macOSDatabaseDirectory().UTF8String; +#else + NSString* location = databaseLocation(); + if ([location isEqualToString:@"ApplicationSupport"]) { + // Library/Application Support is persistent, backed up, and never exposed to the user + // via the Files app, unlike the Documents directory when the app enables file sharing. + NSString* applicationSupport = searchPathDirectory(NSApplicationSupportDirectory); + NSFileManager* fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:applicationSupport]) { + [fileManager createDirectoryAtPath:applicationSupport withIntermediateDirectories:YES attributes:nil error:nil]; + } + return applicationSupport.UTF8String; + } + + if (location != nil && ![location isEqualToString:@"Documents"]) { + NSLog(@"Invalid RNNitroSQLite_DatabaseLocation value provided (%@). Supported values are \"Documents\" and \"ApplicationSupport\". " + @"Falling back to \"Documents\".", + location); + } + return searchPathDirectory(NSDocumentDirectory).UTF8String; +#endif + } +} + +std::optional HybridPlatformContext::getTemporaryDirectory() { + return std::nullopt; +} + +std::optional HybridPlatformContext::getLegacyDatabaseDirectory() { +#if TARGET_OS_OSX + return std::nullopt; +#else + @autoreleasepool { + // Databases created before RNNitroSQLite_DatabaseLocation moved them to Application Support + // still live in Documents. HybridNitroSQLite moves each one when it is opened. + if (appGroupIdentifier() != nil || ![databaseLocation() isEqualToString:@"ApplicationSupport"]) { + return std::nullopt; + } + return std::string(searchPathDirectory(NSDocumentDirectory).UTF8String); + } +#endif +} + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/ios/OnLoad.mm b/packages/react-native-nitro-sqlite/ios/OnLoad.mm deleted file mode 100644 index 58c39394..00000000 --- a/packages/react-native-nitro-sqlite/ios/OnLoad.mm +++ /dev/null @@ -1,99 +0,0 @@ -#import -#import -#import "hybridObjects/HybridNitroSQLite.hpp" - -#if TARGET_OS_OSX -static NSString *macOSDatabaseDirectory(void) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error = nil; - NSURL *applicationSupportURL = [fileManager URLForDirectory:NSApplicationSupportDirectory - inDomain:NSUserDomainMask - appropriateForURL:nil - create:YES - error:&error]; - if (applicationSupportURL == nil) { - @throw [NSException exceptionWithName:@"SQLiteInitializationException" - reason:[NSString stringWithFormat:@"Could not find the Application Support directory: %@", error] - userInfo:nil]; - } - - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - if (bundleIdentifier == nil) { - bundleIdentifier = [[NSProcessInfo processInfo] processName]; - } - - NSURL *databaseURL = [applicationSupportURL URLByAppendingPathComponent:bundleIdentifier isDirectory:YES]; - if (![fileManager createDirectoryAtURL:databaseURL withIntermediateDirectories:YES attributes:nil error:&error]) { - @throw [NSException exceptionWithName:@"SQLiteInitializationException" - reason:[NSString stringWithFormat:@"Could not create the database directory: %@", error] - userInfo:nil]; - } - - return [databaseURL path]; -} -#endif - -@interface OnLoad : NSObject -@end - -@implementation OnLoad - -using namespace margelo::nitro; -using namespace margelo::nitro::rnnitrosqlite; - -+ (void)load { - // Get appGroupID value from Info.plist using key "RNNitroSQLite_AppGroup". - NSString *appGroupID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RNNitroSQLite_AppGroup"]; - NSString *documentPath; - - if (appGroupID != nil) { - // Get the app groups container storage url - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSURL *storeUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:appGroupID]; - - if (storeUrl == nil) { - NSLog(@"Invalid App Group ID provided (%@). Check the value of \"RNNitroSQLite_AppGroup\" in your Info.plist file", appGroupID); - @throw [NSException exceptionWithName:@"SQLiteInitializationException" - reason:@"Error while initializing SQLite database (AppGroup)" - userInfo:nil]; - } - NSLog(@"Configured with AppGroup ID: %@", appGroupID); - - documentPath = [storeUrl path]; - } else { -#if TARGET_OS_OSX - documentPath = macOSDatabaseDirectory(); -#else - NSString *databaseLocation = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RNNitroSQLite_DatabaseLocation"]; - - if ([databaseLocation isEqualToString:@"ApplicationSupport"]) { - // Library/Application Support is persistent, backed up, and never exposed to the user - // via the Files app (unlike the Documents directory, which becomes user-visible when - // the app enables file sharing). - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true); - documentPath = [paths objectAtIndex:0]; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - if (![fileManager fileExistsAtPath:documentPath]) { - [fileManager createDirectoryAtPath:documentPath withIntermediateDirectories:YES attributes:nil error:nil]; - } - - // Databases created before this option was enabled still live in Documents; each one is - // moved over when it is opened (see HybridNitroSQLite::open). - NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) objectAtIndex:0]; - HybridNitroSQLite::migrationDocPath = [documentsDirectory UTF8String]; - } else { - if (databaseLocation != nil && ![databaseLocation isEqualToString:@"Documents"]) { - NSLog(@"Invalid RNNitroSQLite_DatabaseLocation value provided (%@). Supported values are \"Documents\" and \"ApplicationSupport\". Falling back to \"Documents\".", databaseLocation); - } - // Get iOS app's document directory (to safely store database .sqlite3 file) - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true); - documentPath = [paths objectAtIndex:0]; - } -#endif - } - - HybridNitroSQLite::docPath = [documentPath UTF8String]; -} - -@end diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLite+autolinking.cmake b/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLite+autolinking.cmake index d004b400..a5e23273 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLite+autolinking.cmake +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLite+autolinking.cmake @@ -15,7 +15,7 @@ target_sources(RNNitroSQLite PRIVATE "${CMAKE_CURRENT_LIST_DIR}/RNNitroSQLiteOnLoad.cpp" "${CMAKE_CURRENT_LIST_DIR}/../android/c++/HybridNitroSQLiteSpecClass.cpp" "${CMAKE_CURRENT_LIST_DIR}/../android/c++/HybridPreparedStatementSpecClass.cpp" - "${CMAKE_CURRENT_LIST_DIR}/../android/c++/JHybridNitroSQLiteOnLoadSpec.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../android/c++/JHybridPlatformContextSpec.cpp" "${CMAKE_CURRENT_LIST_DIR}/../shared/c++/BatchQueryCommand.cpp" "${CMAKE_CURRENT_LIST_DIR}/../shared/c++/BatchQueryResult.cpp" "${CMAKE_CURRENT_LIST_DIR}/../shared/c++/FileLoadResult.cpp" diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp index f5e252d3..5dc62316 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp @@ -7,16 +7,16 @@ #include #include -#include +#include #include #include namespace margelo::nitro::rnnitrosqlite { void registerAllNatives() { - margelo::nitro::rnnitrosqlite::JHybridNitroSQLiteOnLoadSpec::CxxPart::registerNatives(); + margelo::nitro::rnnitrosqlite::JHybridPlatformContextSpec::CxxPart::registerNatives(); HybridClassRegistry::registerHybridClass("rnnitrosqlite.NitroSQLite", HybridNitroSQLiteSpec::Class::get()); - HybridClassRegistry::registerHybridClass("rnnitrosqlite.NitroSQLiteOnLoad", JHybridNitroSQLiteOnLoadSpec::Class::get()); + HybridClassRegistry::registerHybridClass("rnnitrosqlite.PlatformContext", JHybridPlatformContextSpec::Class::get()); HybridClassRegistry::registerHybridClass("rnnitrosqlite.PreparedStatement", HybridPreparedStatementSpec::Class::get()); } } diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/HybridNitroSQLiteSpecClass.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/HybridNitroSQLiteSpecClass.cpp index e0aaf001..abf341a0 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/HybridNitroSQLiteSpecClass.cpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/HybridNitroSQLiteSpecClass.cpp @@ -31,10 +31,10 @@ namespace margelo::nitro::rnnitrosqlite { namespace { struct HybridNitroSQLiteSpecBindings final { - static std::shared_ptr constructor() { - static_assert(std::is_constructible_v<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>, + static std::shared_ptr constructor(const std::string& databaseDirectory, const std::optional& temporaryDirectory, const std::optional& legacyDatabaseDirectory) { + static_assert(std::is_constructible_v<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite, decltype(databaseDirectory), decltype(temporaryDirectory), decltype(legacyDatabaseDirectory)>, "NitroSQLite: ::margelo::nitro::rnnitrosqlite::HybridNitroSQLite must implement the declared public constructor."); - return std::make_shared<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>(); + return std::make_shared<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>(std::forward(databaseDirectory), std::forward(temporaryDirectory), std::forward(legacyDatabaseDirectory)); } }; } diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.cpp deleted file mode 100644 index b5c7f98e..00000000 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/// -/// JHybridNitroSQLiteOnLoadSpec.cpp -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/margelo/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -#include - -namespace margelo::nitro::rnnitrosqlite { - - JHybridNitroSQLiteOnLoadSpec::~JHybridNitroSQLiteOnLoadSpec() = default; - - void JHybridNitroSQLiteOnLoadSpec::init() { - static const auto __method = JHybridNitroSQLiteOnLoadSpec::JavaPart::javaClassStatic()->getStaticMethod("init_cxx"); - __method(JHybridNitroSQLiteOnLoadSpec::JavaPart::javaClassStatic()); - } - - const std::shared_ptr& JHybridNitroSQLiteOnLoadSpec::Class::get() { - static const auto hybridClass = HybridClass::create([](HybridClass& clazz) { - clazz.registerStaticHybridMethod("init", &JHybridNitroSQLiteOnLoadSpec::init); - }); - return hybridClass; - } - - std::shared_ptr JHybridNitroSQLiteOnLoadSpecJavaPart::getJHybridNitroSQLiteOnLoadSpec() { - auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); - auto castHybridObject = std::dynamic_pointer_cast(hybridObject); - if (castHybridObject == nullptr) [[unlikely]] { - throw std::runtime_error("Failed to downcast JHybridObject to JHybridNitroSQLiteOnLoadSpec!"); - } - return castHybridObject; - } - - jni::local_ref JHybridNitroSQLiteOnLoadSpec::CxxPart::initHybrid(jni::alias_ref jThis) { - return makeCxxInstance(jThis); - } - - jni::alias_ref JHybridNitroSQLiteOnLoadSpec::CxxPart::getClass() { - static const auto __field = CxxPart::javaClassStatic()->getStaticField("implementationClass_cxx"); - static const auto __class = jni::make_global(CxxPart::javaClassStatic()->getStaticFieldValue(__field)); - return __class; - } - - std::shared_ptr JHybridNitroSQLiteOnLoadSpec::CxxPart::createHybridObject(jni::alias_ref javaPart) { - auto castJavaPart = jni::dynamic_ref_cast(javaPart); - if (castJavaPart == nullptr) [[unlikely]] { - throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridNitroSQLiteOnLoadSpec::JavaPart!"); - } - return std::make_shared(castJavaPart); - } - - void JHybridNitroSQLiteOnLoadSpec::CxxPart::registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", JHybridNitroSQLiteOnLoadSpec::CxxPart::initHybrid), - }); - } - - // Properties - - - // Methods - - -} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridPlatformContextSpec.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridPlatformContextSpec.cpp new file mode 100644 index 00000000..3c47428c --- /dev/null +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/JHybridPlatformContextSpec.cpp @@ -0,0 +1,84 @@ +/// +/// JHybridPlatformContextSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/margelo/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include +#include +#include +#include +#include + +namespace margelo::nitro::rnnitrosqlite { + + JHybridPlatformContextSpec::~JHybridPlatformContextSpec() = default; + + jni::local_ref JHybridPlatformContextSpec::getDatabaseDirectory() { + static const auto __method = JHybridPlatformContextSpec::JavaPart::javaClassStatic()->getStaticMethod()>("getDatabaseDirectory_cxx"); + auto __result = __method(JHybridPlatformContextSpec::JavaPart::javaClassStatic()); + return __result; + } + + jni::local_ref> JHybridPlatformContextSpec::getTemporaryDirectory() { + static const auto __method = JHybridPlatformContextSpec::JavaPart::javaClassStatic()->getStaticMethod>()>("getTemporaryDirectory_cxx"); + auto __result = __method(JHybridPlatformContextSpec::JavaPart::javaClassStatic()); + return __result; + } + + jni::local_ref> JHybridPlatformContextSpec::getLegacyDatabaseDirectory() { + static const auto __method = JHybridPlatformContextSpec::JavaPart::javaClassStatic()->getStaticMethod>()>("getLegacyDatabaseDirectory_cxx"); + auto __result = __method(JHybridPlatformContextSpec::JavaPart::javaClassStatic()); + return __result; + } + + const std::shared_ptr& JHybridPlatformContextSpec::Class::get() { + static const auto hybridClass = HybridClass::create([](HybridClass& clazz) { + clazz.registerStaticHybridProperty("databaseDirectory", &JHybridPlatformContextSpec::getDatabaseDirectory); + clazz.registerStaticHybridProperty("temporaryDirectory", &JHybridPlatformContextSpec::getTemporaryDirectory); + clazz.registerStaticHybridProperty("legacyDatabaseDirectory", &JHybridPlatformContextSpec::getLegacyDatabaseDirectory); + }); + return hybridClass; + } + + std::shared_ptr JHybridPlatformContextSpecJavaPart::getJHybridPlatformContextSpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridPlatformContextSpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridPlatformContextSpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + jni::alias_ref JHybridPlatformContextSpec::CxxPart::getClass() { + static const auto __field = CxxPart::javaClassStatic()->getStaticField("implementationClass_cxx"); + static const auto __class = jni::make_global(CxxPart::javaClassStatic()->getStaticFieldValue(__field)); + return __class; + } + + std::shared_ptr JHybridPlatformContextSpec::CxxPart::createHybridObject(jni::alias_ref javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridPlatformContextSpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridPlatformContextSpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridPlatformContextSpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpec.hpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpec.hpp similarity index 56% rename from packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpec.hpp rename to packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpec.hpp index 274882c4..16813a2b 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpec.hpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpec.hpp @@ -1,5 +1,5 @@ /// -/// JHybridNitroSQLiteOnLoadSpec.hpp +/// JHybridPlatformContextSpec.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/margelo/nitro /// Copyright © Marc Rousavy @ Margelo @@ -11,25 +11,29 @@ #include #include #include -#include -#include +#include +#include +#include +#include +#include +#include namespace margelo::nitro::rnnitrosqlite { namespace jni = facebook::jni; - class JHybridNitroSQLiteOnLoadSpec final : public JHybridObject { + class JHybridPlatformContextSpec final : public JHybridObject { public: struct Class { using Base = JHybridObject::Class; - static constexpr const char* name = "NitroSQLiteOnLoad"; + static constexpr const char* name = "PlatformContext"; static const std::shared_ptr& get(); }; public: - using JavaPart = JHybridNitroSQLiteOnLoadSpecJavaPart; + using JavaPart = JHybridPlatformContextSpecJavaPart; struct CxxPart : public jni::HybridClass { - static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec$CxxPart;"; + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/rnnitrosqlite/HybridPlatformContextSpec$CxxPart;"; static jni::local_ref initHybrid(jni::alias_ref jThis); static void registerNatives(); static jni::alias_ref getClass(); @@ -39,20 +43,20 @@ namespace margelo::nitro::rnnitrosqlite { }; public: - explicit JHybridNitroSQLiteOnLoadSpec(jni::alias_ref javaPart): + explicit JHybridPlatformContextSpec(jni::alias_ref javaPart): JHybridObject(javaPart), _javaPart(jni::static_ref_cast(JHybridObject::getJavaPart())) { #ifdef NITRO_DEBUG if (!jni::isSameObject(_javaPart->getClass(), CxxPart::getClass())) [[unlikely]] { - throw std::runtime_error("NitroSQLiteOnLoad requires its configured implementation when inheritance and multiple implementations are disabled."); + throw std::runtime_error("PlatformContext requires its configured implementation when inheritance and multiple implementations are disabled."); } #endif } // Defined in the owning library so RTTI has one identity across modules. - ~JHybridNitroSQLiteOnLoadSpec() override; + ~JHybridPlatformContextSpec() override; public: - inline jni::alias_ref getJavaPart() const noexcept { + inline jni::alias_ref getJavaPart() const noexcept { return _javaPart; } @@ -66,7 +70,9 @@ namespace margelo::nitro::rnnitrosqlite { public: // Constructor and static members - static void init(); + static jni::local_ref getDatabaseDirectory(); + static jni::local_ref> getTemporaryDirectory(); + static jni::local_ref> getLegacyDatabaseDirectory(); protected: const std::shared_ptr& getHybridClass() override { @@ -74,7 +80,7 @@ namespace margelo::nitro::rnnitrosqlite { } private: - jni::alias_ref _javaPart; + jni::alias_ref _javaPart; }; } // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpecJavaPart.hpp b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpecJavaPart.hpp similarity index 52% rename from packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpecJavaPart.hpp rename to packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpecJavaPart.hpp index 4bb05911..d480a538 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridNitroSQLiteOnLoadSpecJavaPart.hpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/c++/include/RNNitroSQLite/private/JHybridPlatformContextSpecJavaPart.hpp @@ -1,5 +1,5 @@ /// -/// JHybridNitroSQLiteOnLoadSpecJavaPart.hpp +/// JHybridPlatformContextSpecJavaPart.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/margelo/nitro /// Copyright © Marc Rousavy @ Margelo @@ -15,12 +15,12 @@ namespace margelo::nitro::rnnitrosqlite { namespace jni = facebook::jni; - class JHybridNitroSQLiteOnLoadSpec; + class JHybridPlatformContextSpec; - struct JHybridNitroSQLiteOnLoadSpecJavaPart : public jni::JavaClass { - using HybridType = JHybridNitroSQLiteOnLoadSpec; - static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec;"; - std::shared_ptr getJHybridNitroSQLiteOnLoadSpec(); + struct JHybridPlatformContextSpecJavaPart : public jni::JavaClass { + using HybridType = JHybridPlatformContextSpec; + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/rnnitrosqlite/HybridPlatformContextSpec;"; + std::shared_ptr getJHybridPlatformContextSpec(); }; } // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec.kt b/packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContextSpec.kt similarity index 50% rename from packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec.kt rename to packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContextSpec.kt index d1dd507c..c5d37e4d 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec.kt +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridPlatformContextSpec.kt @@ -1,5 +1,5 @@ /// -/// HybridNitroSQLiteOnLoadSpec.kt +/// HybridPlatformContextSpec.kt /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/margelo/nitro /// Copyright © Marc Rousavy @ Margelo @@ -11,12 +11,12 @@ import androidx.annotation.Keep import com.facebook.jni.HybridData import com.facebook.proguard.annotations.DoNotStrip import dalvik.annotation.optimization.FastNative -import com.margelo.nitro.rnnitrosqlite.HybridNitroSQLiteOnLoad +import com.margelo.nitro.rnnitrosqlite.HybridPlatformContext import com.margelo.nitro.core.HybridObject /** - * A Kotlin class representing the NitroSQLiteOnLoad HybridObject. - * Implement this abstract class to create Kotlin-based instances of NitroSQLiteOnLoad. + * A Kotlin class representing the PlatformContext HybridObject. + * Implement this abstract class to create Kotlin-based instances of PlatformContext. */ @DoNotStrip @Keep @@ -25,7 +25,7 @@ import com.margelo.nitro.core.HybridObject "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" ) -abstract class HybridNitroSQLiteOnLoadSpec: HybridObject() { +abstract class HybridPlatformContextSpec: HybridObject() { // Properties @@ -34,14 +34,14 @@ abstract class HybridNitroSQLiteOnLoadSpec: HybridObject() { // Default implementation of `HybridObject.toString()` override fun toString(): String { - return "[HybridObject NitroSQLiteOnLoad]" + return "[HybridObject PlatformContext]" } // C++ backing class @DoNotStrip @Keep - protected open class CxxPart(javaPart: HybridNitroSQLiteOnLoadSpec): HybridObject.CxxPart(javaPart) { - // C++ JHybridNitroSQLiteOnLoadSpec::CxxPart::initHybrid(...) + protected open class CxxPart(javaPart: HybridPlatformContextSpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridPlatformContextSpec::CxxPart::initHybrid(...) @FastNative external override fun initHybrid(): HybridData @@ -49,7 +49,7 @@ abstract class HybridNitroSQLiteOnLoadSpec: HybridObject() { @DoNotStrip @Keep @JvmField - val implementationClass_cxx: Class = HybridNitroSQLiteOnLoad::class.java + val implementationClass_cxx: Class = HybridPlatformContext::class.java } } override fun createCxxPart(): CxxPart { @@ -58,24 +58,45 @@ abstract class HybridNitroSQLiteOnLoadSpec: HybridObject() { interface Companion { // Static Properties + val databaseDirectory: String + val temporaryDirectory: String? + + val legacyDatabaseDirectory: String? // Static Methods - fun init(): Unit + } protected companion object Bridge { - protected const val TAG = "HybridNitroSQLiteOnLoadSpec" + protected const val TAG = "HybridPlatformContextSpec" private val implementationCompanion: Companion - get() = HybridNitroSQLiteOnLoad + get() = HybridPlatformContext + + // Static properties + @JvmStatic + @DoNotStrip + @Keep + private fun getDatabaseDirectory_cxx(): String { + val __result = HybridPlatformContext.databaseDirectory + return __result + } + + @JvmStatic + @DoNotStrip + @Keep + private fun getTemporaryDirectory_cxx(): String? { + val __result = HybridPlatformContext.temporaryDirectory + return __result + } - // Static methods @JvmStatic @DoNotStrip @Keep - private fun init_cxx(): Unit { - HybridNitroSQLiteOnLoad.init() + private fun getLegacyDatabaseDirectory_cxx(): String? { + val __result = HybridPlatformContext.legacyDatabaseDirectory + return __result } } } diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/hybrid-classes.json b/packages/react-native-nitro-sqlite/nitrogen/generated/hybrid-classes.json index 79ad40ef..e7fe4069 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/hybrid-classes.json +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/hybrid-classes.json @@ -17,12 +17,19 @@ "language": "c++" } }, - "NitroSQLiteOnLoad": { + "PlatformContext": { "android": { "implementation": { - "className": "HybridNitroSQLiteOnLoad" + "className": "HybridPlatformContext" }, "language": "kotlin" + }, + "ios": { + "implementation": { + "className": "HybridPlatformContext", + "include": "\"hybridObjects/HybridPlatformContext.hpp\"" + }, + "language": "c++" } }, "PreparedStatement": { diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/RNNitroSQLiteAutolinking.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/RNNitroSQLiteAutolinking.cpp index 000a6130..1ec73701 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/RNNitroSQLiteAutolinking.cpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/RNNitroSQLiteAutolinking.cpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace margelo::nitro::rnnitrosqlite { @@ -17,6 +18,7 @@ namespace margelo::nitro::rnnitrosqlite { using namespace margelo::nitro::rnnitrosqlite; HybridClassRegistry::registerHybridClass("rnnitrosqlite.NitroSQLite", HybridNitroSQLiteSpec::Class::get()); + HybridClassRegistry::registerHybridClass("rnnitrosqlite.PlatformContext", HybridPlatformContextSpec::Class::get()); HybridClassRegistry::registerHybridClass("rnnitrosqlite.PreparedStatement", HybridPreparedStatementSpec::Class::get()); } } // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridNitroSQLiteSpecClass.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridNitroSQLiteSpecClass.cpp index e0aaf001..abf341a0 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridNitroSQLiteSpecClass.cpp +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridNitroSQLiteSpecClass.cpp @@ -31,10 +31,10 @@ namespace margelo::nitro::rnnitrosqlite { namespace { struct HybridNitroSQLiteSpecBindings final { - static std::shared_ptr constructor() { - static_assert(std::is_constructible_v<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>, + static std::shared_ptr constructor(const std::string& databaseDirectory, const std::optional& temporaryDirectory, const std::optional& legacyDatabaseDirectory) { + static_assert(std::is_constructible_v<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite, decltype(databaseDirectory), decltype(temporaryDirectory), decltype(legacyDatabaseDirectory)>, "NitroSQLite: ::margelo::nitro::rnnitrosqlite::HybridNitroSQLite must implement the declared public constructor."); - return std::make_shared<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>(); + return std::make_shared<::margelo::nitro::rnnitrosqlite::HybridNitroSQLite>(std::forward(databaseDirectory), std::forward(temporaryDirectory), std::forward(legacyDatabaseDirectory)); } }; } diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpec.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpec.cpp new file mode 100644 index 00000000..9b6400ae --- /dev/null +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpec.cpp @@ -0,0 +1,14 @@ +/// +/// HybridPlatformContextSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/margelo/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include + +namespace margelo::nitro::rnnitrosqlite { + + HybridPlatformContextSpec::~HybridPlatformContextSpec() = default; + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpecClass.cpp b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpecClass.cpp new file mode 100644 index 00000000..e198b508 --- /dev/null +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/HybridPlatformContextSpecClass.cpp @@ -0,0 +1,41 @@ +/// +/// HybridPlatformContextSpecClass.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/margelo/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include +#include +#include +#include +#include "hybridObjects/HybridPlatformContext.hpp" + +namespace margelo::nitro::rnnitrosqlite { + + namespace { + struct HybridPlatformContextSpecBindings final { + static std::string getDatabaseDirectory() { + return ::margelo::nitro::rnnitrosqlite::HybridPlatformContext::getDatabaseDirectory(); + } + + static std::optional getTemporaryDirectory() { + return ::margelo::nitro::rnnitrosqlite::HybridPlatformContext::getTemporaryDirectory(); + } + + static std::optional getLegacyDatabaseDirectory() { + return ::margelo::nitro::rnnitrosqlite::HybridPlatformContext::getLegacyDatabaseDirectory(); + } + }; + } + + const std::shared_ptr& HybridPlatformContextSpec::Class::get() { + static const auto hybridClass = HybridClass::create([](HybridClass& clazz) { + clazz.registerStaticHybridProperty("databaseDirectory", &HybridPlatformContextSpecBindings::getDatabaseDirectory); + clazz.registerStaticHybridProperty("temporaryDirectory", &HybridPlatformContextSpecBindings::getTemporaryDirectory); + clazz.registerStaticHybridProperty("legacyDatabaseDirectory", &HybridPlatformContextSpecBindings::getLegacyDatabaseDirectory); + }); + return hybridClass; + } + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/include/RNNitroSQLite/HybridPlatformContextSpec.hpp b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/include/RNNitroSQLite/HybridPlatformContextSpec.hpp new file mode 100644 index 00000000..d527aab3 --- /dev/null +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/ios/c++/include/RNNitroSQLite/HybridPlatformContextSpec.hpp @@ -0,0 +1,61 @@ +/// +/// HybridPlatformContextSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/margelo/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include +#include + +namespace margelo::nitro::rnnitrosqlite { + + using namespace margelo::nitro; + + /** + * An abstract base class for `PlatformContext` + * Inherit this class to create instances of `HybridPlatformContextSpec` in C++. + * @example + * ```cpp + * class HybridPlatformContext : public HybridPlatformContextSpec { + * public: + * HybridPlatformContext(...) { ... } + * // ... + * }; + * ``` + */ + class HybridPlatformContextSpec : public virtual HybridObject { + public: + struct Class { + using Base = HybridObject::Class; + static constexpr const char* name = "PlatformContext"; + static const std::shared_ptr& get(); + }; + + public: + // Constructor + HybridPlatformContextSpec() = default; + + // Destructor + ~HybridPlatformContextSpec() override; + + public: + // Properties + + + public: + // Methods + + + protected: + // Hybrid Setup + const std::shared_ptr& getHybridClass() override { + return Class::get(); + } + }; + +} // namespace margelo::nitro::rnnitrosqlite diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.cjs b/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.cjs similarity index 70% rename from packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.cjs rename to packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.cjs index 9e230f3e..82cd0f35 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.cjs +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.cjs @@ -1,5 +1,5 @@ /// -/// NitroSQLiteOnLoad.cjs +/// PlatformContext.cjs /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/margelo/nitro /// Copyright © Marc Rousavy @ Margelo @@ -10,9 +10,9 @@ const { NitroModules } = require('react-native-nitro-modules') let hybridClass module.exports = { - get NitroSQLiteOnLoad() { + get PlatformContext() { if (hybridClass === undefined) { - hybridClass = NitroModules.getHybridClass('rnnitrosqlite.NitroSQLiteOnLoad') + hybridClass = NitroModules.getHybridClass('rnnitrosqlite.PlatformContext') } return hybridClass }, diff --git a/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.d.cts b/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.d.cts similarity index 59% rename from packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.d.cts rename to packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.d.cts index d91be463..28fba098 100644 --- a/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/NitroSQLiteOnLoad.d.cts +++ b/packages/react-native-nitro-sqlite/nitrogen/generated/shared/javascript/PlatformContext.d.cts @@ -1,9 +1,9 @@ /// -/// NitroSQLiteOnLoad.d.cts +/// PlatformContext.d.cts /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/margelo/nitro /// Copyright © Marc Rousavy @ Margelo /// -// The spec's `export declare class NitroSQLiteOnLoad` types this runtime export. +// The spec's `export declare class PlatformContext` types this runtime export. export {} diff --git a/packages/react-native-nitro-sqlite/src/OnLoad.android.ts b/packages/react-native-nitro-sqlite/src/OnLoad.android.ts deleted file mode 100644 index b4ebe523..00000000 --- a/packages/react-native-nitro-sqlite/src/OnLoad.android.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { NitroSQLiteOnLoad } from './specs/NitroSQLiteOnLoad.nitro' - -export const init = () => NitroSQLiteOnLoad.init() diff --git a/packages/react-native-nitro-sqlite/src/OnLoad.ts b/packages/react-native-nitro-sqlite/src/OnLoad.ts deleted file mode 100644 index a069caad..00000000 --- a/packages/react-native-nitro-sqlite/src/OnLoad.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Apple platforms initialize the database directory from OnLoad.mm. -export const init = () => {} diff --git a/packages/react-native-nitro-sqlite/src/__tests__/entrypoints.test.ts b/packages/react-native-nitro-sqlite/src/__tests__/entrypoints.test.ts index efac7896..81281252 100644 --- a/packages/react-native-nitro-sqlite/src/__tests__/entrypoints.test.ts +++ b/packages/react-native-nitro-sqlite/src/__tests__/entrypoints.test.ts @@ -1,10 +1,18 @@ jest.mock('react-native-nitro-modules', () => ({ NitroModules: { getHybridClass: jest.fn((name: string) => - name === 'rnnitrosqlite.NitroSQLiteOnLoad' - ? { init: jest.fn() } + name === 'rnnitrosqlite.PlatformContext' + ? { + databaseDirectory: '/data/databases', + temporaryDirectory: '/data/cache', + legacyDatabaseDirectory: undefined, + } : class { open = jest.fn() + constructorArguments: unknown[] + constructor(...args: unknown[]) { + this.constructorArguments = args + } }, ), }, @@ -24,8 +32,6 @@ import { NitroSQLiteExceptionType as ExceptionTypeFromErrors, } from '../errors' import { HybridNitroSQLite } from '../nitro' -import { init as androidInit } from '../OnLoad.android' -import { init as iosInit } from '../OnLoad' describe('entrypoints', () => { it('shares the error class and category constants with the errors entry point', () => { @@ -40,20 +46,16 @@ describe('entrypoints', () => { expect(NitroSQLite.native).toBe(HybridNitroSQLite) expect(NitroSQLite.open).toBe(open) expect(NitroSQLite.open).not.toBe(HybridNitroSQLite.open) - expect(iosInit()).toBeUndefined() }) - it('initializes the Android native loader', () => { - androidInit() - + it('stores databases in the platform directories', () => { expect(NitroModules.getHybridClass).toHaveBeenCalledWith( - 'rnnitrosqlite.NitroSQLiteOnLoad', + 'rnnitrosqlite.PlatformContext', ) - const onLoad = jest - .mocked(NitroModules.getHybridClass) - .mock.results.map((result) => result.value as { init?: jest.Mock }) - .find((value) => 'init' in value) - expect(onLoad?.init).toHaveBeenCalledTimes(1) + expect( + (HybridNitroSQLite as unknown as { constructorArguments: unknown[] }) + .constructorArguments, + ).toEqual(['/data/databases', '/data/cache', undefined]) }) it('publishes built entry points so apps never compile the decorated specs', () => { diff --git a/packages/react-native-nitro-sqlite/src/index.ts b/packages/react-native-nitro-sqlite/src/index.ts index a8c97d47..1f4fa771 100644 --- a/packages/react-native-nitro-sqlite/src/index.ts +++ b/packages/react-native-nitro-sqlite/src/index.ts @@ -3,11 +3,8 @@ import { HybridNitroSQLite } from './nitro' import { open } from './operations/session' import { execute, executeAsync } from './operations/execute' import { prepare } from './operations/prepare' -import { init } from './OnLoad' import { executeBatch, executeBatchAsync } from './operations/executeBatch' -init() - /** Database entry point. Prefer {@link open} for a managed connection. * `native` exposes the Nitro object directly and bypasses the JavaScript queue. */ diff --git a/packages/react-native-nitro-sqlite/src/nitro.ts b/packages/react-native-nitro-sqlite/src/nitro.ts index 1cd966f9..91a5d13c 100644 --- a/packages/react-native-nitro-sqlite/src/nitro.ts +++ b/packages/react-native-nitro-sqlite/src/nitro.ts @@ -1,3 +1,8 @@ import { NitroSQLite } from './specs/NitroSQLite.nitro' +import { PlatformContext } from './specs/PlatformContext.nitro' -export const HybridNitroSQLite = new NitroSQLite() +export const HybridNitroSQLite = new NitroSQLite( + PlatformContext.databaseDirectory, + PlatformContext.temporaryDirectory, + PlatformContext.legacyDatabaseDirectory, +) diff --git a/packages/react-native-nitro-sqlite/src/specs/NitroSQLite.nitro.ts b/packages/react-native-nitro-sqlite/src/specs/NitroSQLite.nitro.ts index 2597563d..ef843e18 100644 --- a/packages/react-native-nitro-sqlite/src/specs/NitroSQLite.nitro.ts +++ b/packages/react-native-nitro-sqlite/src/specs/NitroSQLite.nitro.ts @@ -32,8 +32,20 @@ export * from '#nitro/NitroSQLite' }, }) export declare class NitroSQLite extends HybridObject { - /** Create a root with its own default and independent connections. */ - constructor() + /** Create a root with its own default and independent connections. + * @param databaseDirectory Directory that database names and relative + * locations resolve against, usually `PlatformContext.databaseDirectory`. + * @param temporaryDirectory Directory for SQLite's temporary files. SQLite + * reads it once per process, so the first root that passes one sets it. + * @param legacyDatabaseDirectory Directory that databases are moved out of + * when they are opened, attached, or dropped. + * @throws If SQLite's temporary directory can't be configured. + */ + constructor( + databaseDirectory: string, + temporaryDirectory?: string, + legacyDatabaseDirectory?: string, + ) /** Open a name-based default connection, creating the database unless `readOnly` is true. * @param dbName Database file name and default connection key. diff --git a/packages/react-native-nitro-sqlite/src/specs/NitroSQLiteOnLoad.nitro.ts b/packages/react-native-nitro-sqlite/src/specs/NitroSQLiteOnLoad.nitro.ts deleted file mode 100644 index 3cb3123d..00000000 --- a/packages/react-native-nitro-sqlite/src/specs/NitroSQLiteOnLoad.nitro.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Android, type HybridObject } from 'react-native-nitro-modules' - -export * from '#nitro/NitroSQLiteOnLoad' - -/** Android startup hook that configures the native database directories. */ -@Android({ - language: 'kotlin', - implementation: { className: 'HybridNitroSQLiteOnLoad' }, -}) -export declare class NitroSQLiteOnLoad extends HybridObject { - /** Pass the app's files and cache directories from the application context to SQLite. */ - static init(): void -} diff --git a/packages/react-native-nitro-sqlite/src/specs/PlatformContext.nitro.ts b/packages/react-native-nitro-sqlite/src/specs/PlatformContext.nitro.ts new file mode 100644 index 00000000..735f7b73 --- /dev/null +++ b/packages/react-native-nitro-sqlite/src/specs/PlatformContext.nitro.ts @@ -0,0 +1,31 @@ +import { Android, iOS, type HybridObject } from 'react-native-nitro-modules' + +export * from '#nitro/PlatformContext' + +/** Platform directories that a `NitroSQLite` root stores its databases in. */ +@iOS({ + language: 'c++', + implementation: { + className: 'HybridPlatformContext', + include: '"hybridObjects/HybridPlatformContext.hpp"', + }, +}) +@Android({ + language: 'kotlin', + implementation: { className: 'HybridPlatformContext' }, +}) +export declare class PlatformContext extends HybridObject { + /** Directory that database names and relative locations resolve against. + * On Apple platforms, the `RNNitroSQLite_AppGroup` and + * `RNNitroSQLite_DatabaseLocation` Info.plist keys select it. + * @throws If the configured app group container doesn't exist or the + * directory can't be created. + */ + static readonly databaseDirectory: string + /** Directory for SQLite's temporary files, when the platform has no usable default. */ + static readonly temporaryDirectory?: string + /** Directory that databases are moved out of when they are opened, attached, or dropped. + * Set when the app stores databases somewhere other than earlier versions did. + */ + static readonly legacyDatabaseDirectory?: string +}