react-native-mmkv 4.1.2 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroMmkv.podspec +9 -2
- package/android/CMakeLists.txt +5 -0
- package/android/build.gradle +8 -3
- package/android/src/main/cpp/cpp-adapter.cpp +2 -1
- package/cpp/HybridMMKV.cpp +94 -58
- package/cpp/HybridMMKV.hpp +5 -0
- package/cpp/HybridMMKVFactory.cpp +1 -5
- package/cpp/MMKVTypes.hpp +12 -0
- package/cpp/ManagedMMBuffer.hpp +1 -1
- package/lib/__tests__/hooks.test.js +30 -1
- package/lib/createMMKV/createMMKV.web.js +17 -1
- package/lib/createMMKV/createMockMMKV.js +15 -1
- package/lib/hooks/createMMKVHook.js +9 -18
- package/lib/specs/MMKV.nitro.d.ts +46 -1
- package/lib/specs/MMKVFactory.nitro.d.ts +30 -1
- package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +35 -25
- package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +13 -4
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +20 -26
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +19 -22
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +15 -18
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +0 -1
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +0 -1
- package/nitrogen/generated/shared/c++/Configuration.hpp +13 -2
- package/nitrogen/generated/shared/c++/EncryptionType.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +5 -0
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +8 -0
- package/package.json +3 -3
- package/src/__tests__/hooks.test.tsx +37 -0
- package/src/createMMKV/createMMKV.web.ts +17 -1
- package/src/createMMKV/createMockMMKV.ts +15 -1
- package/src/hooks/createMMKVHook.ts +16 -19
- package/src/specs/MMKV.nitro.ts +46 -1
- package/src/specs/MMKVFactory.nitro.ts +31 -1
|
@@ -6,6 +6,12 @@ import type { MMKV } from './MMKV.nitro';
|
|
|
6
6
|
* - `multi-process`: The MMKV instance may be used from multiple processes, such as app clips, share extensions or background services.
|
|
7
7
|
*/
|
|
8
8
|
export type Mode = 'single-process' | 'multi-process';
|
|
9
|
+
/**
|
|
10
|
+
* Configures the encryption algorithm for the MMKV instance.
|
|
11
|
+
* - `AES-128`: Uses AES-128 encryption (default).
|
|
12
|
+
* - `AES-256`: Uses AES-256 encryption for enhanced security.
|
|
13
|
+
*/
|
|
14
|
+
export type EncryptionType = 'AES-128' | 'AES-256';
|
|
9
15
|
/**
|
|
10
16
|
* Used for configuration of a single MMKV instance.
|
|
11
17
|
*/
|
|
@@ -40,7 +46,7 @@ export interface Configuration {
|
|
|
40
46
|
/**
|
|
41
47
|
* The MMKV instance's encryption/decryption key. By default, MMKV stores all key-values in plain text on file, relying on iOS's sandbox to make sure the file is encrypted. Should you worry about information leaking, you can choose to encrypt MMKV.
|
|
42
48
|
*
|
|
43
|
-
* Encryption keys can have a maximum length of 16 bytes.
|
|
49
|
+
* Encryption keys can have a maximum length of 16 bytes with AES-128 encryption and 32 bytes with AES-256 encryption.
|
|
44
50
|
*
|
|
45
51
|
* @example
|
|
46
52
|
* ```ts
|
|
@@ -50,6 +56,21 @@ export interface Configuration {
|
|
|
50
56
|
* @default undefined
|
|
51
57
|
*/
|
|
52
58
|
encryptionKey?: string;
|
|
59
|
+
/**
|
|
60
|
+
* The encryption algorithm to use when an encryption key is provided.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const secureStorage = createMMKV({
|
|
65
|
+
* id: 'secure-storage',
|
|
66
|
+
* encryptionKey: 'my-encryption-key!',
|
|
67
|
+
* encryptionType: 'AES-256'
|
|
68
|
+
* })
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @default 'AES-128'
|
|
72
|
+
*/
|
|
73
|
+
encryptionType?: EncryptionType;
|
|
53
74
|
/**
|
|
54
75
|
* Configure the processing mode for MMKV.
|
|
55
76
|
*
|
|
@@ -62,6 +83,14 @@ export interface Configuration {
|
|
|
62
83
|
* @default false
|
|
63
84
|
*/
|
|
64
85
|
readOnly?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* If `true`, MMKV will internally compare a value for equality before writing to
|
|
88
|
+
* disk, and if the new value is equal to what is already stored, it will skip
|
|
89
|
+
* the file write.
|
|
90
|
+
* Treat this as an optional performance optimization.
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
compareBeforeSet?: boolean;
|
|
65
94
|
}
|
|
66
95
|
export interface MMKVFactory extends HybridObject<{
|
|
67
96
|
ios: 'c++';
|
|
@@ -22,33 +22,43 @@
|
|
|
22
22
|
namespace margelo::nitro::mmkv {
|
|
23
23
|
|
|
24
24
|
int initialize(JavaVM* vm) {
|
|
25
|
+
return facebook::jni::initialize(vm, []() {
|
|
26
|
+
::margelo::nitro::mmkv::registerAllNatives();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
struct JHybridMMKVPlatformContextSpecImpl: public jni::JavaClass<JHybridMMKVPlatformContextSpecImpl, JHybridMMKVPlatformContextSpec::JavaPart> {
|
|
31
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContext;";
|
|
32
|
+
static std::shared_ptr<JHybridMMKVPlatformContextSpec> create() {
|
|
33
|
+
static auto constructorFn = javaClassStatic()->getConstructor<JHybridMMKVPlatformContextSpecImpl::javaobject()>();
|
|
34
|
+
jni::local_ref<JHybridMMKVPlatformContextSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
|
|
35
|
+
return javaPart->getJHybridMMKVPlatformContextSpec();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
void registerAllNatives() {
|
|
25
40
|
using namespace margelo::nitro;
|
|
26
41
|
using namespace margelo::nitro::mmkv;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
auto instance = object.create();
|
|
48
|
-
return instance->cthis()->shared();
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
});
|
|
42
|
+
|
|
43
|
+
// Register native JNI methods
|
|
44
|
+
margelo::nitro::mmkv::JHybridMMKVPlatformContextSpec::CxxPart::registerNatives();
|
|
45
|
+
|
|
46
|
+
// Register Nitro Hybrid Objects
|
|
47
|
+
HybridObjectRegistry::registerHybridObjectConstructor(
|
|
48
|
+
"MMKVFactory",
|
|
49
|
+
[]() -> std::shared_ptr<HybridObject> {
|
|
50
|
+
static_assert(std::is_default_constructible_v<HybridMMKVFactory>,
|
|
51
|
+
"The HybridObject \"HybridMMKVFactory\" is not default-constructible! "
|
|
52
|
+
"Create a public constructor that takes zero arguments to be able to autolink this HybridObject.");
|
|
53
|
+
return std::make_shared<HybridMMKVFactory>();
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
HybridObjectRegistry::registerHybridObjectConstructor(
|
|
57
|
+
"MMKVPlatformContext",
|
|
58
|
+
[]() -> std::shared_ptr<HybridObject> {
|
|
59
|
+
return JHybridMMKVPlatformContextSpecImpl::create();
|
|
60
|
+
}
|
|
61
|
+
);
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
} // namespace margelo::nitro::mmkv
|
|
@@ -6,20 +6,29 @@
|
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#include <jni.h>
|
|
9
|
+
#include <functional>
|
|
9
10
|
#include <NitroModules/NitroDefines.hpp>
|
|
10
11
|
|
|
11
12
|
namespace margelo::nitro::mmkv {
|
|
12
13
|
|
|
14
|
+
[[deprecated("Use registerNatives() instead.")]]
|
|
15
|
+
int initialize(JavaVM* vm);
|
|
16
|
+
|
|
13
17
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`)
|
|
18
|
+
* Register the native (C++) part of NitroMmkv, and autolinks all Hybrid Objects.
|
|
19
|
+
* Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`),
|
|
20
|
+
* inside a `facebook::jni::initialize(vm, ...)` call.
|
|
16
21
|
* Example:
|
|
17
22
|
* ```cpp (cpp-adapter.cpp)
|
|
18
23
|
* JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
19
|
-
* return
|
|
24
|
+
* return facebook::jni::initialize(vm, []() {
|
|
25
|
+
* // register all NitroMmkv HybridObjects
|
|
26
|
+
* margelo::nitro::mmkv::registerNatives();
|
|
27
|
+
* // any other custom registrations go here.
|
|
28
|
+
* });
|
|
20
29
|
* }
|
|
21
30
|
* ```
|
|
22
31
|
*/
|
|
23
|
-
|
|
32
|
+
void registerAllNatives();
|
|
24
33
|
|
|
25
34
|
} // namespace margelo::nitro::mmkv
|
|
@@ -14,37 +14,31 @@
|
|
|
14
14
|
|
|
15
15
|
namespace margelo::nitro::mmkv {
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
17
|
+
std::shared_ptr<JHybridMMKVPlatformContextSpec> JHybridMMKVPlatformContextSpec::JavaPart::getJHybridMMKVPlatformContextSpec() {
|
|
18
|
+
auto hybridObject = JHybridObject::JavaPart::getJHybridObject();
|
|
19
|
+
auto castHybridObject = std::dynamic_pointer_cast<JHybridMMKVPlatformContextSpec>(hybridObject);
|
|
20
|
+
if (castHybridObject == nullptr) [[unlikely]] {
|
|
21
|
+
throw std::runtime_error("Failed to downcast JHybridObject to JHybridMMKVPlatformContextSpec!");
|
|
22
|
+
}
|
|
23
|
+
return castHybridObject;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return method(_javaPart);
|
|
26
|
+
jni::local_ref<JHybridMMKVPlatformContextSpec::CxxPart::jhybriddata> JHybridMMKVPlatformContextSpec::CxxPart::initHybrid(jni::alias_ref<jhybridobject> jThis) {
|
|
27
|
+
return makeCxxInstance(jThis);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
std::shared_ptr<JHybridObject> JHybridMMKVPlatformContextSpec::CxxPart::createHybridObject(const jni::local_ref<JHybridObject::JavaPart>& javaPart) {
|
|
31
|
+
auto castJavaPart = jni::dynamic_ref_cast<JHybridMMKVPlatformContextSpec::JavaPart>(javaPart);
|
|
32
|
+
if (castJavaPart == nullptr) [[unlikely]] {
|
|
33
|
+
throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridMMKVPlatformContextSpec::JavaPart!");
|
|
35
34
|
}
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
void JHybridMMKVPlatformContextSpec::dispose() noexcept {
|
|
40
|
-
static const auto method = javaClassStatic()->getMethod<void()>("dispose");
|
|
41
|
-
method(_javaPart);
|
|
35
|
+
return std::make_shared<JHybridMMKVPlatformContextSpec>(castJavaPart);
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
void JHybridMMKVPlatformContextSpec::CxxPart::registerNatives() {
|
|
39
|
+
registerHybrid({
|
|
40
|
+
makeNativeMethod("initHybrid", JHybridMMKVPlatformContextSpec::CxxPart::initHybrid),
|
|
41
|
+
});
|
|
48
42
|
}
|
|
49
43
|
|
|
50
44
|
// Properties
|
|
@@ -52,12 +46,12 @@ namespace margelo::nitro::mmkv {
|
|
|
52
46
|
|
|
53
47
|
// Methods
|
|
54
48
|
std::string JHybridMMKVPlatformContextSpec::getBaseDirectory() {
|
|
55
|
-
static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getBaseDirectory");
|
|
49
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getBaseDirectory");
|
|
56
50
|
auto __result = method(_javaPart);
|
|
57
51
|
return __result->toStdString();
|
|
58
52
|
}
|
|
59
53
|
std::optional<std::string> JHybridMMKVPlatformContextSpec::getAppGroupDirectory() {
|
|
60
|
-
static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getAppGroupDirectory");
|
|
54
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getAppGroupDirectory");
|
|
61
55
|
auto __result = method(_javaPart);
|
|
62
56
|
return __result != nullptr ? std::make_optional(__result->toStdString()) : std::nullopt;
|
|
63
57
|
}
|
|
@@ -18,34 +18,33 @@ namespace margelo::nitro::mmkv {
|
|
|
18
18
|
|
|
19
19
|
using namespace facebook;
|
|
20
20
|
|
|
21
|
-
class JHybridMMKVPlatformContextSpec: public
|
|
22
|
-
public virtual HybridMMKVPlatformContextSpec {
|
|
21
|
+
class JHybridMMKVPlatformContextSpec: public virtual HybridMMKVPlatformContextSpec, public virtual JHybridObject {
|
|
23
22
|
public:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
HybridBase
|
|
33
|
-
|
|
23
|
+
struct JavaPart: public jni::JavaClass<JavaPart, JHybridObject::JavaPart> {
|
|
24
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec;";
|
|
25
|
+
std::shared_ptr<JHybridMMKVPlatformContextSpec> getJHybridMMKVPlatformContextSpec();
|
|
26
|
+
};
|
|
27
|
+
struct CxxPart: public jni::HybridClass<CxxPart, JHybridObject::CxxPart> {
|
|
28
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec$CxxPart;";
|
|
29
|
+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
30
|
+
static void registerNatives();
|
|
31
|
+
using HybridBase::HybridBase;
|
|
32
|
+
protected:
|
|
33
|
+
std::shared_ptr<JHybridObject> createHybridObject(const jni::local_ref<JHybridObject::JavaPart>& javaPart) override;
|
|
34
|
+
};
|
|
34
35
|
|
|
35
36
|
public:
|
|
37
|
+
explicit JHybridMMKVPlatformContextSpec(const jni::local_ref<JHybridMMKVPlatformContextSpec::JavaPart>& javaPart):
|
|
38
|
+
HybridObject(HybridMMKVPlatformContextSpec::TAG),
|
|
39
|
+
JHybridObject(javaPart),
|
|
40
|
+
_javaPart(jni::make_global(javaPart)) {}
|
|
36
41
|
~JHybridMMKVPlatformContextSpec() override {
|
|
37
42
|
// Hermes GC can destroy JS objects on a non-JNI Thread.
|
|
38
43
|
jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); });
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
public:
|
|
42
|
-
|
|
43
|
-
bool equals(const std::shared_ptr<HybridObject>& other) override;
|
|
44
|
-
void dispose() noexcept override;
|
|
45
|
-
std::string toString() override;
|
|
46
|
-
|
|
47
|
-
public:
|
|
48
|
-
inline const jni::global_ref<JHybridMMKVPlatformContextSpec::javaobject>& getJavaPart() const noexcept {
|
|
47
|
+
inline const jni::global_ref<JHybridMMKVPlatformContextSpec::JavaPart>& getJavaPart() const noexcept {
|
|
49
48
|
return _javaPart;
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -59,9 +58,7 @@ namespace margelo::nitro::mmkv {
|
|
|
59
58
|
std::optional<std::string> getAppGroupDirectory() override;
|
|
60
59
|
|
|
61
60
|
private:
|
|
62
|
-
|
|
63
|
-
using HybridBase::HybridBase;
|
|
64
|
-
jni::global_ref<JHybridMMKVPlatformContextSpec::javaobject> _javaPart;
|
|
61
|
+
jni::global_ref<JHybridMMKVPlatformContextSpec::JavaPart> _javaPart;
|
|
65
62
|
};
|
|
66
63
|
|
|
67
64
|
} // namespace margelo::nitro::mmkv
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt
CHANGED
|
@@ -24,23 +24,6 @@ import com.margelo.nitro.core.HybridObject
|
|
|
24
24
|
"LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
|
|
25
25
|
)
|
|
26
26
|
abstract class HybridMMKVPlatformContextSpec: HybridObject() {
|
|
27
|
-
@DoNotStrip
|
|
28
|
-
private var mHybridData: HybridData = initHybrid()
|
|
29
|
-
|
|
30
|
-
init {
|
|
31
|
-
super.updateNative(mHybridData)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
override fun updateNative(hybridData: HybridData) {
|
|
35
|
-
mHybridData = hybridData
|
|
36
|
-
super.updateNative(hybridData)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Default implementation of `HybridObject.toString()`
|
|
40
|
-
override fun toString(): String {
|
|
41
|
-
return "[HybridObject MMKVPlatformContext]"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
27
|
// Properties
|
|
45
28
|
|
|
46
29
|
|
|
@@ -53,7 +36,21 @@ abstract class HybridMMKVPlatformContextSpec: HybridObject() {
|
|
|
53
36
|
@Keep
|
|
54
37
|
abstract fun getAppGroupDirectory(): String?
|
|
55
38
|
|
|
56
|
-
|
|
39
|
+
// Default implementation of `HybridObject.toString()`
|
|
40
|
+
override fun toString(): String {
|
|
41
|
+
return "[HybridObject MMKVPlatformContext]"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// C++ backing class
|
|
45
|
+
@DoNotStrip
|
|
46
|
+
@Keep
|
|
47
|
+
protected open class CxxPart(javaPart: HybridMMKVPlatformContextSpec): HybridObject.CxxPart(javaPart) {
|
|
48
|
+
// C++ JHybridMMKVPlatformContextSpec::CxxPart::initHybrid(...)
|
|
49
|
+
external override fun initHybrid(): HybridData
|
|
50
|
+
}
|
|
51
|
+
override fun createCxxPart(): CxxPart {
|
|
52
|
+
return CxxPart(this)
|
|
53
|
+
}
|
|
57
54
|
|
|
58
55
|
companion object {
|
|
59
56
|
protected const val TAG = "HybridMMKVPlatformContextSpec"
|
|
@@ -41,7 +41,7 @@ namespace margelo::nitro::mmkv::bridge::swift {
|
|
|
41
41
|
return optional.has_value();
|
|
42
42
|
}
|
|
43
43
|
inline std::string get_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
|
|
44
|
-
return
|
|
44
|
+
return optional.value();
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// pragma MARK: std::shared_ptr<HybridMMKVPlatformContextSpec>
|
|
@@ -28,11 +28,14 @@
|
|
|
28
28
|
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
29
29
|
#endif
|
|
30
30
|
|
|
31
|
+
// Forward declaration of `EncryptionType` to properly resolve imports.
|
|
32
|
+
namespace margelo::nitro::mmkv { enum class EncryptionType; }
|
|
31
33
|
// Forward declaration of `Mode` to properly resolve imports.
|
|
32
34
|
namespace margelo::nitro::mmkv { enum class Mode; }
|
|
33
35
|
|
|
34
36
|
#include <string>
|
|
35
37
|
#include <optional>
|
|
38
|
+
#include "EncryptionType.hpp"
|
|
36
39
|
#include "Mode.hpp"
|
|
37
40
|
|
|
38
41
|
namespace margelo::nitro::mmkv {
|
|
@@ -45,12 +48,14 @@ namespace margelo::nitro::mmkv {
|
|
|
45
48
|
std::string id SWIFT_PRIVATE;
|
|
46
49
|
std::optional<std::string> path SWIFT_PRIVATE;
|
|
47
50
|
std::optional<std::string> encryptionKey SWIFT_PRIVATE;
|
|
51
|
+
std::optional<EncryptionType> encryptionType SWIFT_PRIVATE;
|
|
48
52
|
std::optional<Mode> mode SWIFT_PRIVATE;
|
|
49
53
|
std::optional<bool> readOnly SWIFT_PRIVATE;
|
|
54
|
+
std::optional<bool> compareBeforeSet SWIFT_PRIVATE;
|
|
50
55
|
|
|
51
56
|
public:
|
|
52
57
|
Configuration() = default;
|
|
53
|
-
explicit Configuration(std::string id, std::optional<std::string> path, std::optional<std::string> encryptionKey, std::optional<Mode> mode, std::optional<bool> readOnly): id(id), path(path), encryptionKey(encryptionKey), mode(mode), readOnly(readOnly) {}
|
|
58
|
+
explicit Configuration(std::string id, std::optional<std::string> path, std::optional<std::string> encryptionKey, std::optional<EncryptionType> encryptionType, std::optional<Mode> mode, std::optional<bool> readOnly, std::optional<bool> compareBeforeSet): id(id), path(path), encryptionKey(encryptionKey), encryptionType(encryptionType), mode(mode), readOnly(readOnly), compareBeforeSet(compareBeforeSet) {}
|
|
54
59
|
|
|
55
60
|
public:
|
|
56
61
|
friend bool operator==(const Configuration& lhs, const Configuration& rhs) = default;
|
|
@@ -69,8 +74,10 @@ namespace margelo::nitro {
|
|
|
69
74
|
JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
|
|
70
75
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path"))),
|
|
71
76
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey"))),
|
|
77
|
+
JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionType"))),
|
|
72
78
|
JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode"))),
|
|
73
|
-
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly")))
|
|
79
|
+
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly"))),
|
|
80
|
+
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet")))
|
|
74
81
|
);
|
|
75
82
|
}
|
|
76
83
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::mmkv::Configuration& arg) {
|
|
@@ -78,8 +85,10 @@ namespace margelo::nitro {
|
|
|
78
85
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::string>::toJSI(runtime, arg.id));
|
|
79
86
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "path"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.path));
|
|
80
87
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.encryptionKey));
|
|
88
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "encryptionType"), JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::toJSI(runtime, arg.encryptionType));
|
|
81
89
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "mode"), JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::toJSI(runtime, arg.mode));
|
|
82
90
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "readOnly"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.readOnly));
|
|
91
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.compareBeforeSet));
|
|
83
92
|
return obj;
|
|
84
93
|
}
|
|
85
94
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -93,8 +102,10 @@ namespace margelo::nitro {
|
|
|
93
102
|
if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
|
|
94
103
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path")))) return false;
|
|
95
104
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey")))) return false;
|
|
105
|
+
if (!JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionType")))) return false;
|
|
96
106
|
if (!JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode")))) return false;
|
|
97
107
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly")))) return false;
|
|
108
|
+
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet")))) return false;
|
|
98
109
|
return true;
|
|
99
110
|
}
|
|
100
111
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// EncryptionType.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#if __has_include(<NitroModules/NitroHash.hpp>)
|
|
11
|
+
#include <NitroModules/NitroHash.hpp>
|
|
12
|
+
#else
|
|
13
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
|
+
#endif
|
|
15
|
+
#if __has_include(<NitroModules/JSIConverter.hpp>)
|
|
16
|
+
#include <NitroModules/JSIConverter.hpp>
|
|
17
|
+
#else
|
|
18
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
19
|
+
#endif
|
|
20
|
+
#if __has_include(<NitroModules/NitroDefines.hpp>)
|
|
21
|
+
#include <NitroModules/NitroDefines.hpp>
|
|
22
|
+
#else
|
|
23
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
namespace margelo::nitro::mmkv {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* An enum which can be represented as a JavaScript union (EncryptionType).
|
|
30
|
+
*/
|
|
31
|
+
enum class EncryptionType {
|
|
32
|
+
AES_128 SWIFT_NAME(aes128) = 0,
|
|
33
|
+
AES_256 SWIFT_NAME(aes256) = 1,
|
|
34
|
+
} CLOSED_ENUM;
|
|
35
|
+
|
|
36
|
+
} // namespace margelo::nitro::mmkv
|
|
37
|
+
|
|
38
|
+
namespace margelo::nitro {
|
|
39
|
+
|
|
40
|
+
// C++ EncryptionType <> JS EncryptionType (union)
|
|
41
|
+
template <>
|
|
42
|
+
struct JSIConverter<margelo::nitro::mmkv::EncryptionType> final {
|
|
43
|
+
static inline margelo::nitro::mmkv::EncryptionType fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
44
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
|
|
45
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
46
|
+
case hashString("AES-128"): return margelo::nitro::mmkv::EncryptionType::AES_128;
|
|
47
|
+
case hashString("AES-256"): return margelo::nitro::mmkv::EncryptionType::AES_256;
|
|
48
|
+
default: [[unlikely]]
|
|
49
|
+
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum EncryptionType - invalid value!");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::mmkv::EncryptionType arg) {
|
|
53
|
+
switch (arg) {
|
|
54
|
+
case margelo::nitro::mmkv::EncryptionType::AES_128: return JSIConverter<std::string>::toJSI(runtime, "AES-128");
|
|
55
|
+
case margelo::nitro::mmkv::EncryptionType::AES_256: return JSIConverter<std::string>::toJSI(runtime, "AES-256");
|
|
56
|
+
default: [[unlikely]]
|
|
57
|
+
throw std::invalid_argument("Cannot convert EncryptionType to JS - invalid value: "
|
|
58
|
+
+ std::to_string(static_cast<int>(arg)) + "!");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
62
|
+
if (!value.isString()) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
|
|
66
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
67
|
+
case hashString("AES-128"):
|
|
68
|
+
case hashString("AES-256"):
|
|
69
|
+
return true;
|
|
70
|
+
default:
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
} // namespace margelo::nitro
|
|
@@ -15,8 +15,11 @@ namespace margelo::nitro::mmkv {
|
|
|
15
15
|
// load custom methods/properties
|
|
16
16
|
registerHybrids(this, [](Prototype& prototype) {
|
|
17
17
|
prototype.registerHybridGetter("id", &HybridMMKVSpec::getId);
|
|
18
|
+
prototype.registerHybridGetter("length", &HybridMMKVSpec::getLength);
|
|
18
19
|
prototype.registerHybridGetter("size", &HybridMMKVSpec::getSize);
|
|
20
|
+
prototype.registerHybridGetter("byteSize", &HybridMMKVSpec::getByteSize);
|
|
19
21
|
prototype.registerHybridGetter("isReadOnly", &HybridMMKVSpec::getIsReadOnly);
|
|
22
|
+
prototype.registerHybridGetter("isEncrypted", &HybridMMKVSpec::getIsEncrypted);
|
|
20
23
|
prototype.registerHybridMethod("set", &HybridMMKVSpec::set);
|
|
21
24
|
prototype.registerHybridMethod("getBoolean", &HybridMMKVSpec::getBoolean);
|
|
22
25
|
prototype.registerHybridMethod("getString", &HybridMMKVSpec::getString);
|
|
@@ -27,6 +30,8 @@ namespace margelo::nitro::mmkv {
|
|
|
27
30
|
prototype.registerHybridMethod("getAllKeys", &HybridMMKVSpec::getAllKeys);
|
|
28
31
|
prototype.registerHybridMethod("clearAll", &HybridMMKVSpec::clearAll);
|
|
29
32
|
prototype.registerHybridMethod("recrypt", &HybridMMKVSpec::recrypt);
|
|
33
|
+
prototype.registerHybridMethod("encrypt", &HybridMMKVSpec::encrypt);
|
|
34
|
+
prototype.registerHybridMethod("decrypt", &HybridMMKVSpec::decrypt);
|
|
30
35
|
prototype.registerHybridMethod("trim", &HybridMMKVSpec::trim);
|
|
31
36
|
prototype.registerHybridMethod("addOnValueChangedListener", &HybridMMKVSpec::addOnValueChangedListener);
|
|
32
37
|
prototype.registerHybridMethod("importAllFrom", &HybridMMKVSpec::importAllFrom);
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
14
|
#endif
|
|
15
15
|
|
|
16
|
+
// Forward declaration of `EncryptionType` to properly resolve imports.
|
|
17
|
+
namespace margelo::nitro::mmkv { enum class EncryptionType; }
|
|
16
18
|
// Forward declaration of `Listener` to properly resolve imports.
|
|
17
19
|
namespace margelo::nitro::mmkv { struct Listener; }
|
|
18
20
|
// Forward declaration of `HybridMMKVSpec` to properly resolve imports.
|
|
@@ -23,6 +25,7 @@ namespace margelo::nitro::mmkv { class HybridMMKVSpec; }
|
|
|
23
25
|
#include <variant>
|
|
24
26
|
#include <optional>
|
|
25
27
|
#include <vector>
|
|
28
|
+
#include "EncryptionType.hpp"
|
|
26
29
|
#include "Listener.hpp"
|
|
27
30
|
#include <functional>
|
|
28
31
|
#include <memory>
|
|
@@ -56,8 +59,11 @@ namespace margelo::nitro::mmkv {
|
|
|
56
59
|
public:
|
|
57
60
|
// Properties
|
|
58
61
|
virtual std::string getId() = 0;
|
|
62
|
+
virtual double getLength() = 0;
|
|
59
63
|
virtual double getSize() = 0;
|
|
64
|
+
virtual double getByteSize() = 0;
|
|
60
65
|
virtual bool getIsReadOnly() = 0;
|
|
66
|
+
virtual bool getIsEncrypted() = 0;
|
|
61
67
|
|
|
62
68
|
public:
|
|
63
69
|
// Methods
|
|
@@ -71,6 +77,8 @@ namespace margelo::nitro::mmkv {
|
|
|
71
77
|
virtual std::vector<std::string> getAllKeys() = 0;
|
|
72
78
|
virtual void clearAll() = 0;
|
|
73
79
|
virtual void recrypt(const std::optional<std::string>& key) = 0;
|
|
80
|
+
virtual void encrypt(const std::string& key, std::optional<EncryptionType> encryptionType) = 0;
|
|
81
|
+
virtual void decrypt() = 0;
|
|
74
82
|
virtual void trim() = 0;
|
|
75
83
|
virtual Listener addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) = 0;
|
|
76
84
|
virtual double importAllFrom(const std::shared_ptr<HybridMMKVSpec>& other) = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mmkv",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "⚡️ The fastest key/value storage for React Native.",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
"eslint": "^8.57.0",
|
|
68
68
|
"eslint-config-prettier": "^9.1.0",
|
|
69
69
|
"eslint-plugin-prettier": "^5.2.1",
|
|
70
|
-
"nitrogen": "0.
|
|
70
|
+
"nitrogen": "0.35.0",
|
|
71
71
|
"prettier": "^3.3.3",
|
|
72
72
|
"react": "19.1.1",
|
|
73
73
|
"react-native": "0.82.0",
|
|
74
|
-
"react-native-nitro-modules": "0.
|
|
74
|
+
"react-native-nitro-modules": "0.35.0",
|
|
75
75
|
"typescript": "^5.8.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
renderHook,
|
|
8
8
|
screen,
|
|
9
9
|
cleanup,
|
|
10
|
+
waitFor,
|
|
10
11
|
} from '@testing-library/react-native'
|
|
11
12
|
import { createMMKV, useMMKVNumber, useMMKVString } from '..'
|
|
12
13
|
|
|
@@ -92,3 +93,39 @@ test('functional updates to hooks', () => {
|
|
|
92
93
|
'4',
|
|
93
94
|
])
|
|
94
95
|
})
|
|
96
|
+
|
|
97
|
+
test('useMMKV hook does not miss updates that happen during subscription setup', async () => {
|
|
98
|
+
const raceKey = 'race-key'
|
|
99
|
+
const raceMMKV = createMMKV()
|
|
100
|
+
|
|
101
|
+
let simulatedRaceDone = false
|
|
102
|
+
const originalSubscribe = raceMMKV.addOnValueChangedListener.bind(raceMMKV)
|
|
103
|
+
raceMMKV.addOnValueChangedListener = ((listener) => {
|
|
104
|
+
if (!simulatedRaceDone) {
|
|
105
|
+
simulatedRaceDone = true
|
|
106
|
+
raceMMKV.set(raceKey, 'updated-before-subscribe')
|
|
107
|
+
}
|
|
108
|
+
return originalSubscribe(listener)
|
|
109
|
+
}) as typeof raceMMKV.addOnValueChangedListener
|
|
110
|
+
|
|
111
|
+
const { result } = renderHook(() => useMMKVString(raceKey, raceMMKV))
|
|
112
|
+
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
expect(result.current[0]).toBe('updated-before-subscribe')
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('useMMKV hook stays consistent during rapid updates', async () => {
|
|
119
|
+
const raceKey = 'rapid-key'
|
|
120
|
+
const { result } = renderHook(() => useMMKVNumber(raceKey, mmkv))
|
|
121
|
+
|
|
122
|
+
act(() => {
|
|
123
|
+
for (let i = 1; i <= 100; i++) {
|
|
124
|
+
mmkv.set(raceKey, i)
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
await waitFor(() => {
|
|
129
|
+
expect(result.current[0]).toBe(100)
|
|
130
|
+
})
|
|
131
|
+
})
|