react-native-mmkv 4.1.1 → 4.2.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.
Files changed (31) hide show
  1. package/NitroMmkv.podspec +1 -1
  2. package/android/build.gradle +3 -2
  3. package/android/src/main/cpp/cpp-adapter.cpp +2 -1
  4. package/cpp/HybridMMKV.cpp +55 -12
  5. package/cpp/HybridMMKV.hpp +5 -0
  6. package/lib/__tests__/hooks.test.js +30 -1
  7. package/lib/createMMKV/createMMKV.web.js +17 -1
  8. package/lib/createMMKV/createMockMMKV.js +15 -1
  9. package/lib/hooks/createMMKVHook.js +9 -18
  10. package/lib/specs/MMKV.nitro.d.ts +46 -1
  11. package/lib/specs/MMKVFactory.nitro.d.ts +22 -1
  12. package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +35 -25
  13. package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +13 -4
  14. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +20 -26
  15. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +19 -22
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +15 -18
  17. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +1 -1
  18. package/nitrogen/generated/ios/NitroMmkvAutolinking.swift +8 -7
  19. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +2 -3
  20. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +0 -1
  21. package/nitrogen/generated/shared/c++/Configuration.hpp +8 -1
  22. package/nitrogen/generated/shared/c++/EncryptionType.hpp +76 -0
  23. package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +5 -0
  24. package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +8 -0
  25. package/package.json +3 -3
  26. package/src/__tests__/hooks.test.tsx +37 -0
  27. package/src/createMMKV/createMMKV.web.ts +17 -1
  28. package/src/createMMKV/createMockMMKV.ts +15 -1
  29. package/src/hooks/createMMKVHook.ts +16 -19
  30. package/src/specs/MMKV.nitro.ts +46 -1
  31. package/src/specs/MMKVFactory.nitro.ts +23 -1
@@ -14,37 +14,31 @@
14
14
 
15
15
  namespace margelo::nitro::mmkv {
16
16
 
17
- jni::local_ref<JHybridMMKVPlatformContextSpec::jhybriddata> JHybridMMKVPlatformContextSpec::initHybrid(jni::alias_ref<jhybridobject> jThis) {
18
- return makeCxxInstance(jThis);
19
- }
20
-
21
- void JHybridMMKVPlatformContextSpec::registerNatives() {
22
- registerHybrid({
23
- makeNativeMethod("initHybrid", JHybridMMKVPlatformContextSpec::initHybrid),
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
- size_t JHybridMMKVPlatformContextSpec::getExternalMemorySize() noexcept {
28
- static const auto method = javaClassStatic()->getMethod<jlong()>("getMemorySize");
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
- bool JHybridMMKVPlatformContextSpec::equals(const std::shared_ptr<HybridObject>& other) {
33
- if (auto otherCast = std::dynamic_pointer_cast<JHybridMMKVPlatformContextSpec>(other)) {
34
- return _javaPart == otherCast->_javaPart;
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 false;
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
- std::string JHybridMMKVPlatformContextSpec::toString() {
45
- static const auto method = javaClassStatic()->getMethod<jni::JString()>("toString");
46
- auto javaString = method(_javaPart);
47
- return javaString->toStdString();
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 jni::HybridClass<JHybridMMKVPlatformContextSpec, JHybridObject>,
22
- public virtual HybridMMKVPlatformContextSpec {
21
+ class JHybridMMKVPlatformContextSpec: public virtual HybridMMKVPlatformContextSpec, public virtual JHybridObject {
23
22
  public:
24
- static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec;";
25
- static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
26
- static void registerNatives();
27
-
28
- protected:
29
- // C++ constructor (called from Java via `initHybrid()`)
30
- explicit JHybridMMKVPlatformContextSpec(jni::alias_ref<jhybridobject> jThis) :
31
- HybridObject(HybridMMKVPlatformContextSpec::TAG),
32
- HybridBase(jThis),
33
- _javaPart(jni::make_global(jThis)) {}
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
- size_t getExternalMemorySize() noexcept override;
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
- friend HybridBase;
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
@@ -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
- private external fun initHybrid(): HybridData
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 *optional;
44
+ return optional.value();
45
45
  }
46
46
 
47
47
  // pragma MARK: std::shared_ptr<HybridMMKVPlatformContextSpec>
@@ -5,16 +5,13 @@
5
5
  /// Copyright © Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import NitroModules
9
+
10
+ // TODO: Use empty enums once Swift supports exporting them as namespaces
11
+ // See: https://github.com/swiftlang/swift/pull/83616
8
12
  public final class NitroMmkvAutolinking {
9
13
  public typealias bridge = margelo.nitro.mmkv.bridge.swift
10
14
 
11
- /**
12
- * Creates an instance of a Swift class that implements `HybridMMKVPlatformContextSpec`,
13
- * and wraps it in a Swift class that can directly interop with C++ (`HybridMMKVPlatformContextSpec_cxx`)
14
- *
15
- * This is generated by Nitrogen and will initialize the class specified
16
- * in the `"autolinking"` property of `nitro.json` (in this case, `HybridMMKVPlatformContext`).
17
- */
18
15
  public static func createMMKVPlatformContext() -> bridge.std__shared_ptr_HybridMMKVPlatformContextSpec_ {
19
16
  let hybridObject = HybridMMKVPlatformContext()
20
17
  return { () -> bridge.std__shared_ptr_HybridMMKVPlatformContextSpec_ in
@@ -22,4 +19,8 @@ public final class NitroMmkvAutolinking {
22
19
  return __cxxWrapped.getCxxPart()
23
20
  }()
24
21
  }
22
+
23
+ public static func isMMKVPlatformContextRecyclable() -> Bool {
24
+ return HybridMMKVPlatformContext.self is any RecyclableView.Type
25
+ }
25
26
  }
@@ -5,7 +5,6 @@
5
5
  /// Copyright © Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
- import Foundation
9
8
  import NitroModules
10
9
 
11
10
  /// See ``HybridMMKVPlatformContextSpec``
@@ -31,14 +30,14 @@ open class HybridMMKVPlatformContextSpec_base {
31
30
  public init() { }
32
31
  public func getCxxWrapper() -> HybridMMKVPlatformContextSpec_cxx {
33
32
  #if DEBUG
34
- guard self is HybridMMKVPlatformContextSpec else {
33
+ guard self is any HybridMMKVPlatformContextSpec else {
35
34
  fatalError("`self` is not a `HybridMMKVPlatformContextSpec`! Did you accidentally inherit from `HybridMMKVPlatformContextSpec_base` instead of `HybridMMKVPlatformContextSpec`?")
36
35
  }
37
36
  #endif
38
37
  if let cxxWrapper = self.cxxWrapper {
39
38
  return cxxWrapper
40
39
  } else {
41
- let cxxWrapper = HybridMMKVPlatformContextSpec_cxx(self as! HybridMMKVPlatformContextSpec)
40
+ let cxxWrapper = HybridMMKVPlatformContextSpec_cxx(self as! any HybridMMKVPlatformContextSpec)
42
41
  self.cxxWrapper = cxxWrapper
43
42
  return cxxWrapper
44
43
  }
@@ -5,7 +5,6 @@
5
5
  /// Copyright © Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
- import Foundation
9
8
  import NitroModules
10
9
 
11
10
  /**
@@ -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,13 @@ 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;
50
54
 
51
55
  public:
52
56
  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) {}
57
+ 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): id(id), path(path), encryptionKey(encryptionKey), encryptionType(encryptionType), mode(mode), readOnly(readOnly) {}
54
58
 
55
59
  public:
56
60
  friend bool operator==(const Configuration& lhs, const Configuration& rhs) = default;
@@ -69,6 +73,7 @@ namespace margelo::nitro {
69
73
  JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
70
74
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path"))),
71
75
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey"))),
76
+ JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionType"))),
72
77
  JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode"))),
73
78
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly")))
74
79
  );
@@ -78,6 +83,7 @@ namespace margelo::nitro {
78
83
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::string>::toJSI(runtime, arg.id));
79
84
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "path"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.path));
80
85
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.encryptionKey));
86
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "encryptionType"), JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::toJSI(runtime, arg.encryptionType));
81
87
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "mode"), JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::toJSI(runtime, arg.mode));
82
88
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "readOnly"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.readOnly));
83
89
  return obj;
@@ -93,6 +99,7 @@ namespace margelo::nitro {
93
99
  if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
94
100
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path")))) return false;
95
101
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionKey")))) return false;
102
+ if (!JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionType")))) return false;
96
103
  if (!JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode")))) return false;
97
104
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly")))) return false;
98
105
  return true;
@@ -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.1.1",
3
+ "version": "4.2.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.32.1",
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.32.1",
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
+ })
@@ -39,8 +39,18 @@ export function createMMKV(
39
39
 
40
40
  return {
41
41
  id: config.id,
42
- size: 0,
42
+ get length(): number {
43
+ return getLocalStorage().length
44
+ },
45
+ get size(): number {
46
+ return this.byteSize
47
+ },
48
+ get byteSize(): number {
49
+ // esimate - assumes UTF8
50
+ return JSON.stringify(getLocalStorage()).length
51
+ },
43
52
  isReadOnly: false,
53
+ isEncrypted: false,
44
54
  clearAll: () => {
45
55
  const storage = getLocalStorage()
46
56
  const keys = Object.keys(storage)
@@ -100,6 +110,12 @@ export function createMMKV(
100
110
  recrypt: () => {
101
111
  throw new Error('`recrypt(..)` is not supported on Web!')
102
112
  },
113
+ encrypt: () => {
114
+ throw new Error('`encrypt(..)` is not supported on Web!')
115
+ },
116
+ decrypt: () => {
117
+ throw new Error('`decrypt(..)` is not supported on Web!')
118
+ },
103
119
  trim: () => {
104
120
  // no-op
105
121
  },
@@ -18,10 +18,18 @@ export function createMockMMKV(
18
18
 
19
19
  return {
20
20
  id: config.id,
21
- get size(): number {
21
+ get length(): number {
22
22
  return storage.size
23
23
  },
24
+ get size(): number {
25
+ return this.byteSize
26
+ },
27
+ get byteSize(): number {
28
+ // esimate - assumes UTF8
29
+ return JSON.stringify(storage).length
30
+ },
24
31
  isReadOnly: false,
32
+ isEncrypted: false,
25
33
  clearAll: () => {
26
34
  const keysBefore = storage.keys()
27
35
  storage.clear()
@@ -63,6 +71,12 @@ export function createMockMMKV(
63
71
  recrypt: () => {
64
72
  console.warn('Encryption is not supported in mocked MMKV instances!')
65
73
  },
74
+ encrypt: () => {
75
+ console.warn('Encryption is not supported in mocked MMKV instances!')
76
+ },
77
+ decrypt: () => {
78
+ console.warn('Encryption is not supported in mocked MMKV instances!')
79
+ },
66
80
  trim: () => {
67
81
  // no-op
68
82
  },
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useState } from 'react'
1
+ import { useCallback, useSyncExternalStore } from 'react'
2
2
  import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
3
3
  import type { MMKV } from '../specs/MMKV.nitro'
4
4
 
@@ -13,14 +13,21 @@ export function createMMKVHook<
13
13
  ): [value: T, setValue: (value: TSetAction) => void] => {
14
14
  const mmkv = instance ?? getDefaultMMKVInstance()
15
15
 
16
- const [bump, setBump] = useState(0)
17
- const value = useMemo(() => {
18
- // bump is here as an additional outside dependency, so this useMemo
19
- // re-computes the value each time bump changes, effectively acting as a hint
20
- // that the outside value (storage) has changed. setting bump refreshes this value.
21
- bump
22
- return getter(mmkv, key)
23
- }, [mmkv, key, bump])
16
+ const value = useSyncExternalStore(
17
+ useCallback(
18
+ (onStoreChange: () => void) => {
19
+ const listener = mmkv.addOnValueChangedListener((changedKey) => {
20
+ if (changedKey === key) {
21
+ onStoreChange()
22
+ }
23
+ })
24
+ return () => listener.remove()
25
+ },
26
+ [key, mmkv]
27
+ ),
28
+ useCallback(() => getter(mmkv, key), [key, mmkv]),
29
+ useCallback(() => getter(mmkv, key), [key, mmkv])
30
+ )
24
31
 
25
32
  // update value by user set
26
33
  const set = useCallback(
@@ -51,16 +58,6 @@ export function createMMKVHook<
51
58
  [key, mmkv]
52
59
  )
53
60
 
54
- // update value if it changes somewhere else (second hook, same key)
55
- useEffect(() => {
56
- const listener = mmkv.addOnValueChangedListener((changedKey) => {
57
- if (changedKey === key) {
58
- setBump((b) => b + 1)
59
- }
60
- })
61
- return () => listener.remove()
62
- }, [key, mmkv])
63
-
64
61
  return [value, set]
65
62
  }
66
63
  }