Open pcboy opened 5 years ago
Hi @pcboy - I've run into the same issue, as there doesn't appear to be any way to use an arbitrary location for the database. If you happen to still need to do this, you can patch in a hardcoded "Shared" location
option pretty easily (replacing YOUR_SHARED_GROUP_NAME
with your group container name):
--- a/node_modules/react-native-sqlite-storage/lib/sqlite.core.js
+++ b/node_modules/react-native-sqlite-storage/lib/sqlite.core.js
@@ -707,7 +707,8 @@ SQLitePluginTransaction.prototype.abortFromQ = function(sqlerror) {
dblocations = {
'default' : 'nosync',
'Documents' : 'docs',
- 'Library' : 'libs'
+ 'Library' : 'libs',
+ 'Shared' : 'shared'
};
SQLiteFactory = function(){};
--- a/node_modules/react-native-sqlite-storage/src/ios/SQLite.m
+++ b/node_modules/react-native-sqlite-storage/src/ios/SQLite.m
@@ -119,6 +119,13 @@ - (id) init
[appDBPaths setObject: libs forKey:@"nosync"];
}
}
+
+ NSURL* groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"YOUR_SHARED_GROUP_NAME"];
+ if (groupURL != NULL) {
+ NSString* shared = groupURL.path;
+ RCTLog(@"Detected Shared path: %@", shared);
+ [appDBPaths setObject: shared forKey:@"shared"];
+ }
}
return self;
}
@@ -226,6 +233,9 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
}
}
#endif
+
+ sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
+
// Attempt to read the SQLite master table [to support SQLCipher version]:
if(sqlite3_exec(db, (const char*)"SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
NSValue *dbPointer = [NSValue valueWithPointer:db];
then you can open the database as:
SQLite.openDatabase({ name: "something.db", location: "Shared" })
Thanks for those patches @jordoh. It seems to work well for the app we're developing here too. It would be great to submit a PR to try and get this supported in the package on NPM. I have forked off and was intending to do that myself, however soon realized that I have no idea how to make the group ID not hardcoded in the native code - my experience with the native platforms are so far mostly non-existent.
Is this something you (or anyone else with a bit more understanding) would be interested in doing, or willing to guide me a bit in doing?
Hi @poltak - I don't have the cycles at the moment, but it should be relatively straightforward to make it configurable. There are a couple ways that come to mind:
Pass the app group name through to the library. This one is a bit tricky because SQLite.m sets up a mapping of location names (e.g. "shared ") to paths on init and the JS code just references those location names. You could pass the app group name through to open
and delete
so it can be passed to getDBPath
and used dynamically, then expose an additional option in the JS interface.
A simpler approach, building off the patch above, would be adding an "AppGroupName" key to your Info.plist
, which could then be read prior to calling containerURLForSecurityApplicationGroupIdentifier
with:
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroupName"];
I've updated the patch above to include a subtle difference: switching the database to WAL journaling mode with: sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
This is necessary due to an obscure iOS behavior where processes will be terminated if they are backgrounded while holding a lock on a file in a shared container, unless the file is a SQLite database in WAL mode (presumably Apple is using such databases themselves, hence the exception, which appears to be tied to reading the header of the SQLite database to check for WAL mode).
Without WAL mode, you'll see crashes in the wild if the app is backgrounded while the database is being modified, e.g.:
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0xdead10cc
Termination Description: SPRINGBOARD, com.yourapp was task-suspended with locked system files: | /var/mobile/Containers/Shared/AppGroup/****.db | ProcessVisibility: Background | ProcessState: Suspended
with a thread writing to the database:
Thread 14 name:
Thread 14:
0 libsystem_kernel.dylib 0x00000001b2e78408 fsync + 8
1 libsqlite3.dylib 0x00000001b373e3ec unixSync + 268 (sqlite3.c:37614)
2 libsqlite3.dylib 0x00000001b3748e2c syncJournal + 516 (sqlite3.c:21998)
3 libsqlite3.dylib 0x00000001b373dcf8 sqlite3PagerCommitPhaseOne + 1288 (sqlite3.c:62699)
4 libsqlite3.dylib 0x00000001b3728154 sqlite3BtreeCommitPhaseOne + 164 (sqlite3.c:72229)
5 libsqlite3.dylib 0x00000001b36f3df4 sqlite3VdbeHalt + 2784 (sqlite3.c:83547)
6 libsqlite3.dylib 0x00000001b3720b00 sqlite3VdbeExec + 59944 (sqlite3.c:89508)
7 libsqlite3.dylib 0x00000001b3710b40 sqlite3_step + 444 (sqlite3.c:86851)
8 yourapp 0x0000000102e75d4c -[SQLite executeSqlWithDict:andArgs:] + 1492 (SQLite.m:516)
9 yourapp 0x0000000102e750e4 -[SQLite executeSqlBatch:success:error:] + 432 (SQLite.m:419)
10 libdispatch.dylib 0x00000001b2d18a38 _dispatch_call_block_and_release + 24 (init.c:1372)
11 libdispatch.dylib 0x00000001b2d197d4 _dispatch_client_callout + 16 (object.m:511)
12 libdispatch.dylib 0x00000001b2cbdc80 _dispatch_queue_override_invoke + 684 (inline_internal.h:2441)
13 libdispatch.dylib 0x00000001b2cca030 _dispatch_root_queue_drain + 372 (inline_internal.h:2482)
14 libdispatch.dylib 0x00000001b2cca8d4 _dispatch_worker_thread2 + 128 (queue.c:6072)
15 libsystem_pthread.dylib 0x00000001b2efa1b4 _pthread_wqthread + 464 (pthread.c:2361)
16 libsystem_pthread.dylib 0x00000001b2efccd4 start_wqthread + 4
Thanks for your help @jordoh. I successfully implemented your 2nd approach in https://github.com/WorldBrain/react-native-sqlite-storage/commit/7d6c2fc64f5f48910a33e28dff24b0f286581b2e
@jordoh thanks!!
Has a possibility to add this solution at source code from a pull request??
HI @LulinhaReparador - @poltak has an open PR for this in https://github.com/andpor/react-native-sqlite-storage/pull/374.
Hey there, could it be possible to allow me to just specify the full path as a string? I already have the shared path in my application for other reasons so it would be convenient if I could simply just supply the string that I already have.
I'm trying to open a db from a path looking like:
/private/var/mobile/Containers/Shared/AppGroup/F4CECCE9-569A-4AB5-B0D7-1C7E705295F3/default.sqlite
Both my app and my call directory extension are using the same app group. So I want the database to be in there.
Problem is SQLite.openDatabase doesn't seem to take an absolute path. There is always a lot of logic involved to find the proper path. Am I doing something wrong? (not an iOS dev)