setSharedItem, getSharedItem and removeSharedItem — the three actions behind the page's _craftSharedKeychain* globals.
The name is iOS's, and it promises more than Android delivers
On iOS this is a keychain access group. Android has no such thing, so the shim uses a plain SharedPreferences file per group — not encrypted, unlike secureSet/secureGet next door, which do use EncryptedSharedPreferences. Worth stating given what the page-facing name suggests. Ported as-is rather than changed: making it encrypted would silently strand anything already written.
The group is a filename
val prefsName = if (group.isNotEmpty()) "craft_shared_$group" else "craft_shared"group is whatever the page passed, and it becomes the preferences file's name. isNotEmpty() is a length check rather than a blank check, so a group of one space is a group and its file is named with the space in it.
That naming is the only part of this that can silently disagree between the two implementations — everything else is a JNI call whose result the JVM decides — so it is a pure function with its own test, and it is computed before any JNI call happens.
An absent key resolves rather than rejects
prefs.getString(key, null) returning null becomes {value: null}, not an error: a page reading a key it never wrote has done nothing wrong. The shim writes that as two separate evaluateJavascript calls differing only in the argument, so here it is one function and the null is a value rather than a branch.
An empty string is not the absent case, and the test says so — that distinction is the one a rewrite would lose.
apply rather than commit
Both writers use apply, which writes on a background thread and returns void. A failure is not observable at the call site in either language, so the resolve the shim sends is a promise about having asked, not about having written. Reproduced, and written down rather than quietly upgraded to commit.
Testing
zig build test:android passes with the ratchet at 79. Both mutations fired before this was committed: collapsing the {value: null} branch into the string branch, and dropping the group from the file name, each fail a named test. Both libcraft.so targets still build with no NDK.