There is a question on the Kotlin Slack about the option for the Java SDK to load the native library from the application bundle, a requirement for the macOS App Store.
Currently the native library is extracted from the JAR to the scratchDir, optionally specified in CouchbaseLite.init. scratchDir is required to be writable and is also used to setC4TmpDirPath, which I’m assuming does need to write to the directory, disallowing setting scratchDir to an app bundle location containing the native lib.
Would it be possible to split the native lib directory from the scratchDir in an optional additional CouchbaseLite.init overload?
It surely is possible it just adds more complexity for the standard use cases. There has not yet been talk of supporting the app store use case for Java, though if enough people want it it will probably be implemented.
All it needs is a different init function that takes the path of the native libs and loads them directly. All the steps for extracting the libs, setting permissions and checking the md5 hashes shouldn’t be necessary.
I’m not entirely sure of what else the scratchDir does but I believe it’s not needed in this case. Other than that default and scratch dir aren’t touched. Here is a working example using reflection that got accepted and is now available in the macOs appstore.
fun initCustom(
debug: Boolean,
defaultDbDir: File,
scratchDir: File,
nativeLibDir: String,
) {
val klass = Class.forName("com.couchbase.lite.internal.CouchbaseLiteInternal")
val initialized = klass.getDeclaredField("INITIALIZED")
initialized.isAccessible = true
val initializedValue = initialized.get(null) as AtomicBoolean
if (initializedValue.getAndSet(true)) return
klass.getDeclaredField("debugging").apply { isAccessible = true }.set(null, debug)
klass.getDeclaredField("defaultDbDir").apply { isAccessible = true }.set(null, FileUtils.verifyDir(defaultDbDir))
System.loadLibrary("$nativeLibDir/libLiteCore.dylib")
if (System.getProperty("os.arch").contains("aarch64", true))
System.loadLibrary("$nativeLibDir/aarch64/libLiteCoreJNI.dylib")
else System.loadLibrary("$nativeLibDir/x86/libLiteCoreJNI.dylib")
klass.getDeclaredMethod("setC4TmpDirPath", File::class.java).apply {
isAccessible = true
}.invoke(null, scratchDir)
}
actual fun initDatabase(): Database {
if (System.getProperty("os.name")
.contains("mac", true) && System.getProperty("compose.application.resources.dir") != null
) {
initCustom(
false,
File(dataDirectory.toString()),
File(dataDirectory.toString()),
System.getProperty("compose.application.resources.dir")
)
} else {
CouchbaseLite.init(
false, File(dataDirectory.toString()),
File(dataDirectory.toString())
)
}
return Database("fluense", DatabaseConfigurationFactory.newConfig(dataDirectory.toString()))
}
(using Kotlin Compose and Kotbase)
I suggested splitting the nativeLibDir from the scratchDir and test modifying CouchbaseLiteInternal.init by using reflection to access private APIs. Sounds like this works well and satisfies Apple’s App Store requirements. It’s great to see developers successfully publishing apps to multiple platforms from the same Kotlin codebase, using Couchbase Lite with Kotbase.
Hopefully an additional function overload won’t add too much complexity for the standard use cases. But one way to actually simplify this entire API would be to remove the necessity to call it entirely for the default init behavior. I’ve obviated the need in Kotbase by calling init in a static initializer for JVM and using the AndroidX Startup library to initialize with a Context reference on Android.
just for completeness because I forgot to update here: It needs to be System.load() instead of System.loadLibrary(). loadLibrary only takes a library name and the former takes a filepath
I’ve obviated the need in Kotbase
I had no idea about this project (unsurprising since I don’t normally work with Android directly). It seems like it would be great to integrate into the library directly.