react-native-mmkv 4.0.0-beta.9 → 4.0.1

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 (35) hide show
  1. package/NitroMmkv.podspec +9 -1
  2. package/android/build.gradle +3 -3
  3. package/cpp/HybridMMKV.cpp +40 -9
  4. package/cpp/HybridMMKV.hpp +2 -1
  5. package/lib/createMMKV/createMMKV.js +2 -2
  6. package/lib/createMMKV/createMMKV.web.d.ts +1 -1
  7. package/lib/createMMKV/createMMKV.web.js +18 -4
  8. package/lib/createMMKV/createMockMMKV.d.ts +6 -0
  9. package/lib/createMMKV/{createMMKV.mock.js → createMockMMKV.js} +8 -5
  10. package/lib/specs/MMKV.nitro.d.ts +16 -11
  11. package/nitrogen/generated/android/NitroMmkv+autolinking.cmake +7 -4
  12. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +6 -0
  13. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +1 -0
  14. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +7 -2
  15. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.cpp +3 -2
  16. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +2 -2
  17. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +3 -0
  18. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +7 -0
  19. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +9 -1
  20. package/nitrogen/generated/shared/c++/Configuration.hpp +8 -0
  21. package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +1 -0
  22. package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +2 -3
  23. package/nitrogen/generated/shared/c++/Listener.hpp +8 -0
  24. package/package.json +7 -8
  25. package/src/createMMKV/createMMKV.ts +2 -2
  26. package/src/createMMKV/createMMKV.web.ts +19 -4
  27. package/src/createMMKV/{createMMKV.mock.ts → createMockMMKV.ts} +10 -5
  28. package/src/specs/MMKV.nitro.ts +16 -11
  29. package/app.plugin.js +0 -1
  30. package/lib/createMMKV/createMMKV.mock.d.ts +0 -5
  31. package/lib/expo-plugin/withMMKV.cjs +0 -26
  32. package/lib/expo-plugin/withMMKV.d.cts +0 -3
  33. package/lib/expo-plugin/withMMKV.d.ts +0 -3
  34. package/lib/expo-plugin/withMMKV.js +0 -17
  35. package/src/expo-plugin/withMMKV.cts +0 -31
package/NitroMmkv.podspec CHANGED
@@ -25,7 +25,15 @@ Pod::Spec.new do |s|
25
25
  # Add MMKV Core dependency
26
26
  s.compiler_flags = '-x objective-c++'
27
27
  s.libraries = 'z', 'c++'
28
- s.dependency 'MMKVCore', '>= 2.2.3'
28
+ s.dependency 'MMKVCore', '2.2.4'
29
+
30
+ # TODO: Remove when no one uses RN 0.79 anymore
31
+ # Add support for React Native 0.79 or below
32
+ s.pod_target_xcconfig = {
33
+ "HEADER_SEARCH_PATHS" => ["${PODS_ROOT}/RCT-Folly"],
34
+ "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES",
35
+ "OTHER_CPLUSPLUSFLAGS" => "$(inherited) -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1"
36
+ }
29
37
 
30
38
  load 'nitrogen/generated/ios/NitroMmkv+autolinking.rb'
31
39
  add_nitrogen_files(s)
@@ -5,13 +5,13 @@ buildscript {
5
5
  }
6
6
 
7
7
  dependencies {
8
- classpath "com.android.tools.build:gradle:8.12.1"
8
+ classpath "com.android.tools.build:gradle:8.13.0"
9
9
  }
10
10
  }
11
11
 
12
12
  def reactNativeArchitectures() {
13
13
  def value = rootProject.getProperties().get("reactNativeArchitectures")
14
- return value ? value.split(",") : ["x86_64", "arm64-v8a"]
14
+ return value ? value.split(",") : ["x86", "x86_64", "armeabi-v7a", "arm64-v8a"]
15
15
  }
16
16
 
17
17
  def isNewArchitectureEnabled() {
@@ -139,6 +139,6 @@ dependencies {
139
139
  implementation project(":react-native-nitro-modules")
140
140
 
141
141
  // Add a dependency on mmkv core (this ships a C++ prefab)
142
- implementation "com.tencent:mmkv-shared:2.2.3"
142
+ implementation "io.github.zhongwuzw:mmkv:2.2.4"
143
143
  }
144
144
 
@@ -55,6 +55,9 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
55
55
  }
56
56
  }
57
57
 
58
+ std::string HybridMMKV::getId() {
59
+ return instance->mmapID();
60
+ }
58
61
  double HybridMMKV::getSize() {
59
62
  return instance->actualSize();
60
63
  }
@@ -70,15 +73,33 @@ struct overloaded : Ts... {
70
73
  template <class... Ts>
71
74
  overloaded(Ts...) -> overloaded<Ts...>;
72
75
 
73
- void HybridMMKV::set(const std::string& key, const std::variant<std::string, double, bool, std::shared_ptr<ArrayBuffer>>& value) {
76
+ void HybridMMKV::set(const std::string& key, const std::variant<bool, std::shared_ptr<ArrayBuffer>, std::string, double>& value) {
77
+ if (key.empty()) [[unlikely]] {
78
+ throw std::runtime_error("Cannot set a value for an empty key!");
79
+ }
80
+
74
81
  // Pattern-match each potential value in std::variant
75
- std::visit(overloaded{[&](const std::string& string) { instance->set(string, key); }, [&](double number) { instance->set(number, key); },
76
- [&](bool b) { instance->set(b, key); },
77
- [&](const std::shared_ptr<ArrayBuffer>& buf) {
78
- MMBuffer buffer(buf->data(), buf->size(), MMBufferCopyFlag::MMBufferNoCopy);
79
- instance->set(std::move(buffer), key);
80
- }},
81
- value);
82
+ bool didSet = std::visit(overloaded{[&](bool b) {
83
+ // boolean
84
+ return instance->set(b, key);
85
+ },
86
+ [&](const std::shared_ptr<ArrayBuffer>& buf) {
87
+ // ArrayBuffer
88
+ MMBuffer buffer(buf->data(), buf->size(), MMBufferCopyFlag::MMBufferNoCopy);
89
+ return instance->set(std::move(buffer), key);
90
+ },
91
+ [&](const std::string& string) {
92
+ // string
93
+ return instance->set(string, key);
94
+ },
95
+ [&](double number) {
96
+ // number
97
+ return instance->set(number, key);
98
+ }},
99
+ value);
100
+ if (!didSet) {
101
+ throw std::runtime_error("Failed to set value for key \"" + key + "\"!");
102
+ }
82
103
 
83
104
  // Notify on changed
84
105
  MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
@@ -130,13 +151,23 @@ bool HybridMMKV::contains(const std::string& key) {
130
151
  return instance->containsKey(key);
131
152
  }
132
153
  bool HybridMMKV::remove(const std::string& key) {
133
- return instance->removeValueForKey(key);
154
+ bool wasRemoved = instance->removeValueForKey(key);
155
+ if (wasRemoved) {
156
+ // Notify on changed
157
+ MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
158
+ }
159
+ return wasRemoved;
134
160
  }
135
161
  std::vector<std::string> HybridMMKV::getAllKeys() {
136
162
  return instance->allKeys();
137
163
  }
138
164
  void HybridMMKV::clearAll() {
165
+ auto keysBefore = getAllKeys();
139
166
  instance->clearAll();
167
+ for (const auto& key : keysBefore) {
168
+ // Notify on changed
169
+ MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
170
+ }
140
171
  }
141
172
  void HybridMMKV::recrypt(const std::optional<std::string>& key) {
142
173
  bool successful = false;
@@ -19,12 +19,13 @@ public:
19
19
 
20
20
  public:
21
21
  // Properties
22
+ std::string getId() override;
22
23
  double getSize() override;
23
24
  bool getIsReadOnly() override;
24
25
 
25
26
  public:
26
27
  // Methods
27
- void set(const std::string& key, const std::variant<std::string, double, bool, std::shared_ptr<ArrayBuffer>>& value) override;
28
+ void set(const std::string& key, const std::variant<bool, std::shared_ptr<ArrayBuffer>, std::string, double>& value) override;
28
29
  std::optional<bool> getBoolean(const std::string& key) override;
29
30
  std::optional<std::string> getString(const std::string& key) override;
30
31
  std::optional<double> getNumber(const std::string& key) override;
@@ -2,13 +2,13 @@ import { NitroModules } from 'react-native-nitro-modules';
2
2
  import { Platform } from 'react-native';
3
3
  import { addMemoryWarningListener } from '../addMemoryWarningListener/addMemoryWarningListener';
4
4
  import { isTest } from '../isTest';
5
- import { createMockMMKV } from './createMMKV.mock';
5
+ import { createMockMMKV } from './createMockMMKV';
6
6
  let factory;
7
7
  let platformContext;
8
8
  export function createMMKV(configuration) {
9
9
  if (isTest()) {
10
10
  // In a test environment, we mock the MMKV instance.
11
- return createMockMMKV();
11
+ return createMockMMKV(configuration);
12
12
  }
13
13
  if (platformContext == null) {
14
14
  // Lazy-init the platform-context HybridObject
@@ -1,3 +1,3 @@
1
1
  import type { MMKV } from '../specs/MMKV.nitro';
2
2
  import type { Configuration } from '../specs/MMKVFactory.nitro';
3
- export declare function createMMKV(config: Configuration): MMKV;
3
+ export declare function createMMKV(config?: Configuration): MMKV;
@@ -12,7 +12,7 @@ const hasAccessToLocalStorage = () => {
12
12
  };
13
13
  const KEY_WILDCARD = '\\';
14
14
  const inMemoryStorage = new Map();
15
- export function createMMKV(config) {
15
+ export function createMMKV(config = { id: 'mmkv.default' }) {
16
16
  if (config.encryptionKey != null) {
17
17
  throw new Error("MMKV: 'encryptionKey' is not supported on Web!");
18
18
  }
@@ -55,18 +55,34 @@ export function createMMKV(config) {
55
55
  }
56
56
  return `${keyPrefix}${key}`;
57
57
  };
58
+ const callListeners = (key) => {
59
+ listeners.forEach((l) => l(key));
60
+ };
58
61
  return {
62
+ id: config.id,
63
+ size: 0,
64
+ isReadOnly: false,
59
65
  clearAll: () => {
60
66
  const keys = Object.keys(storage());
61
67
  for (const key of keys) {
62
68
  if (key.startsWith(keyPrefix)) {
63
69
  storage().removeItem(key);
70
+ callListeners(key);
64
71
  }
65
72
  }
66
73
  },
67
- remove: (key) => storage().removeItem(prefixedKey(key)) ?? false,
74
+ remove: (key) => {
75
+ storage().removeItem(prefixedKey(key));
76
+ const wasRemoved = storage().getItem(prefixedKey(key)) === null;
77
+ if (wasRemoved)
78
+ callListeners(key);
79
+ return wasRemoved;
80
+ },
68
81
  set: (key, value) => {
82
+ if (key === '')
83
+ throw new Error('Cannot set a value for an empty key!');
69
84
  storage().setItem(prefixedKey(key), value.toString());
85
+ callListeners(key);
70
86
  },
71
87
  getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
72
88
  getNumber: (key) => {
@@ -97,8 +113,6 @@ export function createMMKV(config) {
97
113
  recrypt: () => {
98
114
  throw new Error('`recrypt(..)` is not supported on Web!');
99
115
  },
100
- size: 0,
101
- isReadOnly: false,
102
116
  trim: () => {
103
117
  // no-op
104
118
  },
@@ -0,0 +1,6 @@
1
+ import type { MMKV } from '../specs/MMKV.nitro';
2
+ import type { Configuration } from '../specs/MMKVFactory.nitro';
3
+ /**
4
+ * Mock MMKV instance when used in a Jest/Test environment.
5
+ */
6
+ export declare function createMockMMKV(config?: Configuration): MMKV;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Mock MMKV instance when used in a Jest/Test environment.
3
3
  */
4
- export function createMockMMKV() {
4
+ export function createMockMMKV(config = { id: 'mmkv.default' }) {
5
5
  const storage = new Map();
6
6
  const listeners = new Set();
7
7
  const notifyListeners = (key) => {
@@ -10,6 +10,11 @@ export function createMockMMKV() {
10
10
  });
11
11
  };
12
12
  return {
13
+ id: config.id,
14
+ get size() {
15
+ return storage.size;
16
+ },
17
+ isReadOnly: false,
13
18
  clearAll: () => {
14
19
  const keysBefore = storage.keys();
15
20
  storage.clear();
@@ -26,6 +31,8 @@ export function createMockMMKV() {
26
31
  return deleted;
27
32
  },
28
33
  set: (key, value) => {
34
+ if (key === '')
35
+ throw new Error('Cannot set a value for an empty key!');
29
36
  storage.set(key, value);
30
37
  notifyListeners(key);
31
38
  },
@@ -50,10 +57,6 @@ export function createMockMMKV() {
50
57
  recrypt: () => {
51
58
  console.warn('Encryption is not supported in mocked MMKV instances!');
52
59
  },
53
- get size() {
54
- return storage.size;
55
- },
56
- isReadOnly: false,
57
60
  trim: () => {
58
61
  // no-op
59
62
  },
@@ -7,9 +7,23 @@ export interface MMKV extends HybridObject<{
7
7
  android: 'c++';
8
8
  }> {
9
9
  /**
10
- * Set a value for the given `key`.
10
+ * Get the ID of this {@linkcode MMKV} instance.
11
+ */
12
+ readonly id: string;
13
+ /**
14
+ * Get the current total size of the storage, in bytes.
15
+ */
16
+ readonly size: number;
17
+ /**
18
+ * Returns whether this instance is in read-only mode or not.
19
+ * If this is `true`, you can only use "get"-functions.
20
+ */
21
+ readonly isReadOnly: boolean;
22
+ /**
23
+ * Set a {@linkcode value} for the given {@linkcode key}.
11
24
  *
12
- * @throws an Error if the value cannot be set.
25
+ * @throws an Error if the {@linkcode key} is empty.
26
+ * @throws an Error if the {@linkcode value} cannot be set.
13
27
  */
14
28
  set(key: string, value: boolean | string | number | ArrayBuffer): void;
15
29
  /**
@@ -75,15 +89,6 @@ export interface MMKV extends HybridObject<{
75
89
  * In most applications, this is not needed at all.
76
90
  */
77
91
  trim(): void;
78
- /**
79
- * Get the current total size of the storage, in bytes.
80
- */
81
- readonly size: number;
82
- /**
83
- * Returns whether this instance is in read-only mode or not.
84
- * If this is `true`, you can only use "get"-functions.
85
- */
86
- readonly isReadOnly: boolean;
87
92
  /**
88
93
  * Adds a value changed listener. The Listener will be called whenever any value
89
94
  * in this storage instance changes (set or delete).
@@ -13,6 +13,12 @@
13
13
  # include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroMmkv+autolinking.cmake)
14
14
  # ```
15
15
 
16
+ # Define a flag to check if we are building properly
17
+ add_definitions(-DBUILDING_NITROMMKV_WITH_GENERATED_CMAKE_PROJECT)
18
+
19
+ # Enable Raw Props parsing in react-native (for Nitro Views)
20
+ add_definitions(-DRN_SERIALIZABLE_STATE)
21
+
16
22
  # Add all headers that were generated by Nitrogen
17
23
  include_directories(
18
24
  "../nitrogen/generated/shared/c++"
@@ -34,12 +40,9 @@ target_sources(
34
40
  ../nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp
35
41
  )
36
42
 
37
- # Define a flag to check if we are building properly
38
- add_definitions(-DBUILDING_NITROMMKV_WITH_GENERATED_CMAKE_PROJECT)
39
-
40
43
  # From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake
41
44
  # Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake
42
- target_compile_definitions(
45
+ target_compile_definitions(
43
46
  NitroMmkv PRIVATE
44
47
  -DFOLLY_NO_CONFIG=1
45
48
  -DFOLLY_HAVE_CLOCK_GETTIME=1
@@ -34,6 +34,12 @@ namespace margelo::nitro::mmkv {
34
34
  method(_javaPart);
35
35
  }
36
36
 
37
+ std::string JHybridMMKVPlatformContextSpec::toString() {
38
+ static const auto method = javaClassStatic()->getMethod<jni::JString()>("toString");
39
+ auto javaString = method(_javaPart);
40
+ return javaString->toStdString();
41
+ }
42
+
37
43
  // Properties
38
44
 
39
45
 
@@ -41,6 +41,7 @@ namespace margelo::nitro::mmkv {
41
41
  public:
42
42
  size_t getExternalMemorySize() noexcept override;
43
43
  void dispose() noexcept override;
44
+ std::string toString() override;
44
45
 
45
46
  public:
46
47
  inline const jni::global_ref<JHybridMMKVPlatformContextSpec::javaobject>& getJavaPart() const noexcept {
@@ -10,7 +10,7 @@ package com.margelo.nitro.mmkv
10
10
  import androidx.annotation.Keep
11
11
  import com.facebook.jni.HybridData
12
12
  import com.facebook.proguard.annotations.DoNotStrip
13
- import com.margelo.nitro.core.*
13
+ import com.margelo.nitro.core.HybridObject
14
14
 
15
15
  /**
16
16
  * A Kotlin class representing the MMKVPlatformContext HybridObject.
@@ -36,6 +36,11 @@ abstract class HybridMMKVPlatformContextSpec: HybridObject() {
36
36
  super.updateNative(hybridData)
37
37
  }
38
38
 
39
+ // Default implementation of `HybridObject.toString()`
40
+ override fun toString(): String {
41
+ return "[HybridObject MMKVPlatformContext]"
42
+ }
43
+
39
44
  // Properties
40
45
 
41
46
 
@@ -51,6 +56,6 @@ abstract class HybridMMKVPlatformContextSpec: HybridObject() {
51
56
  private external fun initHybrid(): HybridData
52
57
 
53
58
  companion object {
54
- private const val TAG = "HybridMMKVPlatformContextSpec"
59
+ protected const val TAG = "HybridMMKVPlatformContextSpec"
55
60
  }
56
61
  }
@@ -10,15 +10,16 @@
10
10
  // Include C++ implementation defined types
11
11
  #include "HybridMMKVPlatformContextSpecSwift.hpp"
12
12
  #include "NitroMmkv-Swift-Cxx-Umbrella.hpp"
13
+ #include <NitroModules/NitroDefines.hpp>
13
14
 
14
15
  namespace margelo::nitro::mmkv::bridge::swift {
15
16
 
16
17
  // pragma MARK: std::shared_ptr<HybridMMKVPlatformContextSpec>
17
- std::shared_ptr<HybridMMKVPlatformContextSpec> create_std__shared_ptr_HybridMMKVPlatformContextSpec_(void* _Nonnull swiftUnsafePointer) noexcept {
18
+ std::shared_ptr<HybridMMKVPlatformContextSpec> create_std__shared_ptr_HybridMMKVPlatformContextSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
18
19
  NitroMmkv::HybridMMKVPlatformContextSpec_cxx swiftPart = NitroMmkv::HybridMMKVPlatformContextSpec_cxx::fromUnsafe(swiftUnsafePointer);
19
20
  return std::make_shared<margelo::nitro::mmkv::HybridMMKVPlatformContextSpecSwift>(swiftPart);
20
21
  }
21
- void* _Nonnull get_std__shared_ptr_HybridMMKVPlatformContextSpec_(std__shared_ptr_HybridMMKVPlatformContextSpec_ cppType) noexcept {
22
+ void* NON_NULL get_std__shared_ptr_HybridMMKVPlatformContextSpec_(std__shared_ptr_HybridMMKVPlatformContextSpec_ cppType) {
22
23
  std::shared_ptr<margelo::nitro::mmkv::HybridMMKVPlatformContextSpecSwift> swiftWrapper = std::dynamic_pointer_cast<margelo::nitro::mmkv::HybridMMKVPlatformContextSpecSwift>(cppType);
23
24
  #ifdef NITRO_DEBUG
24
25
  if (swiftWrapper == nullptr) [[unlikely]] {
@@ -49,8 +49,8 @@ namespace margelo::nitro::mmkv::bridge::swift {
49
49
  * Specialized version of `std::shared_ptr<HybridMMKVPlatformContextSpec>`.
50
50
  */
51
51
  using std__shared_ptr_HybridMMKVPlatformContextSpec_ = std::shared_ptr<HybridMMKVPlatformContextSpec>;
52
- std::shared_ptr<HybridMMKVPlatformContextSpec> create_std__shared_ptr_HybridMMKVPlatformContextSpec_(void* _Nonnull swiftUnsafePointer) noexcept;
53
- void* _Nonnull get_std__shared_ptr_HybridMMKVPlatformContextSpec_(std__shared_ptr_HybridMMKVPlatformContextSpec_ cppType) noexcept;
52
+ std::shared_ptr<HybridMMKVPlatformContextSpec> create_std__shared_ptr_HybridMMKVPlatformContextSpec_(void* NON_NULL swiftUnsafePointer) noexcept;
53
+ void* NON_NULL get_std__shared_ptr_HybridMMKVPlatformContextSpec_(std__shared_ptr_HybridMMKVPlatformContextSpec_ cppType);
54
54
 
55
55
  // pragma MARK: std::weak_ptr<HybridMMKVPlatformContextSpec>
56
56
  using std__weak_ptr_HybridMMKVPlatformContextSpec_ = std::weak_ptr<HybridMMKVPlatformContextSpec>;
@@ -51,6 +51,9 @@ namespace margelo::nitro::mmkv {
51
51
  void dispose() noexcept override {
52
52
  _swiftPart.dispose();
53
53
  }
54
+ std::string toString() override {
55
+ return _swiftPart.toString();
56
+ }
54
57
 
55
58
  public:
56
59
  // Properties
@@ -18,6 +18,13 @@ public protocol HybridMMKVPlatformContextSpec_protocol: HybridObject {
18
18
  func getAppGroupDirectory() throws -> String?
19
19
  }
20
20
 
21
+ public extension HybridMMKVPlatformContextSpec_protocol {
22
+ /// Default implementation of ``HybridObject.toString``
23
+ func toString() -> String {
24
+ return "[HybridObject MMKVPlatformContext]"
25
+ }
26
+ }
27
+
21
28
  /// See ``HybridMMKVPlatformContextSpec``
22
29
  open class HybridMMKVPlatformContextSpec_base {
23
30
  private weak var cxxWrapper: HybridMMKVPlatformContextSpec_cxx? = nil
@@ -76,7 +76,7 @@ open class HybridMMKVPlatformContextSpec_cxx {
76
76
  */
77
77
  public func getCxxPart() -> bridge.std__shared_ptr_HybridMMKVPlatformContextSpec_ {
78
78
  let cachedCxxPart = self.__cxxPart.lock()
79
- if cachedCxxPart.__convertToBool() {
79
+ if Bool(fromCxx: cachedCxxPart) {
80
80
  return cachedCxxPart
81
81
  } else {
82
82
  let newCxxPart = bridge.create_std__shared_ptr_HybridMMKVPlatformContextSpec_(self.toUnsafe())
@@ -105,6 +105,14 @@ open class HybridMMKVPlatformContextSpec_cxx {
105
105
  self.__implementation.dispose()
106
106
  }
107
107
 
108
+ /**
109
+ * Call toString() on the Swift class.
110
+ */
111
+ @inline(__always)
112
+ public func toString() -> String {
113
+ return self.__implementation.toString()
114
+ }
115
+
108
116
  // Properties
109
117
 
110
118
 
@@ -17,6 +17,11 @@
17
17
  #else
18
18
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
19
  #endif
20
+ #if __has_include(<NitroModules/JSIHelpers.hpp>)
21
+ #include <NitroModules/JSIHelpers.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
20
25
 
21
26
  // Forward declaration of `Mode` to properly resolve imports.
22
27
  namespace margelo::nitro::mmkv { enum class Mode; }
@@ -74,6 +79,9 @@ namespace margelo::nitro {
74
79
  return false;
75
80
  }
76
81
  jsi::Object obj = value.getObject(runtime);
82
+ if (!nitro::isPlainObject(runtime, obj)) {
83
+ return false;
84
+ }
77
85
  if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, "id"))) return false;
78
86
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "path"))) return false;
79
87
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "encryptionKey"))) return false;
@@ -14,6 +14,7 @@ namespace margelo::nitro::mmkv {
14
14
  HybridObject::loadHybridMethods();
15
15
  // load custom methods/properties
16
16
  registerHybrids(this, [](Prototype& prototype) {
17
+ prototype.registerHybridGetter("id", &HybridMMKVSpec::getId);
17
18
  prototype.registerHybridGetter("size", &HybridMMKVSpec::getSize);
18
19
  prototype.registerHybridGetter("isReadOnly", &HybridMMKVSpec::getIsReadOnly);
19
20
  prototype.registerHybridMethod("set", &HybridMMKVSpec::set);
@@ -13,8 +13,6 @@
13
13
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
14
  #endif
15
15
 
16
- // Forward declaration of `ArrayBuffer` to properly resolve imports.
17
- namespace NitroModules { class ArrayBuffer; }
18
16
  // Forward declaration of `Listener` to properly resolve imports.
19
17
  namespace margelo::nitro::mmkv { struct Listener; }
20
18
 
@@ -53,12 +51,13 @@ namespace margelo::nitro::mmkv {
53
51
 
54
52
  public:
55
53
  // Properties
54
+ virtual std::string getId() = 0;
56
55
  virtual double getSize() = 0;
57
56
  virtual bool getIsReadOnly() = 0;
58
57
 
59
58
  public:
60
59
  // Methods
61
- virtual void set(const std::string& key, const std::variant<std::string, double, bool, std::shared_ptr<ArrayBuffer>>& value) = 0;
60
+ virtual void set(const std::string& key, const std::variant<bool, std::shared_ptr<ArrayBuffer>, std::string, double>& value) = 0;
62
61
  virtual std::optional<bool> getBoolean(const std::string& key) = 0;
63
62
  virtual std::optional<std::string> getString(const std::string& key) = 0;
64
63
  virtual std::optional<double> getNumber(const std::string& key) = 0;
@@ -17,6 +17,11 @@
17
17
  #else
18
18
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
19
  #endif
20
+ #if __has_include(<NitroModules/JSIHelpers.hpp>)
21
+ #include <NitroModules/JSIHelpers.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
20
25
 
21
26
 
22
27
 
@@ -59,6 +64,9 @@ namespace margelo::nitro {
59
64
  return false;
60
65
  }
61
66
  jsi::Object obj = value.getObject(runtime);
67
+ if (!nitro::isPlainObject(runtime, obj)) {
68
+ return false;
69
+ }
62
70
  if (!JSIConverter<std::function<void()>>::canConvert(runtime, obj.getProperty(runtime, "remove"))) return false;
63
71
  return true;
64
72
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-mmkv",
3
- "version": "4.0.0-beta.9",
4
- "description": "react-native-mmkv",
3
+ "version": "4.0.1",
4
+ "description": "⚡️ The fastest key/value storage for React Native.",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
7
7
  "types": "lib/index.d.ts",
@@ -31,7 +31,6 @@
31
31
  "README.md"
32
32
  ],
33
33
  "scripts": {
34
- "postinstall": "tsc || exit 0;",
35
34
  "typecheck": "tsc --noEmit",
36
35
  "clean": "rm -rf android/build node_modules/**/android/build lib",
37
36
  "lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
@@ -61,18 +60,18 @@
61
60
  },
62
61
  "devDependencies": {
63
62
  "@expo/config-plugins": "^10.1.2",
64
- "@react-native/eslint-config": "0.81.0",
63
+ "@react-native/eslint-config": "0.82.0",
65
64
  "@testing-library/react-native": "^13.3.1",
66
65
  "@types/jest": "^29.5.12",
67
66
  "@types/react": "^19.0.6",
68
67
  "eslint": "^8.57.0",
69
68
  "eslint-config-prettier": "^9.1.0",
70
69
  "eslint-plugin-prettier": "^5.2.1",
71
- "nitrogen": "^0.29.4",
70
+ "nitrogen": "0.31.7",
72
71
  "prettier": "^3.3.3",
73
- "react": "19.1.0",
74
- "react-native": "0.81.0",
75
- "react-native-nitro-modules": "^0.29.4",
72
+ "react": "19.1.1",
73
+ "react-native": "0.82.0",
74
+ "react-native-nitro-modules": "0.31.7",
76
75
  "typescript": "^5.8.3"
77
76
  },
78
77
  "peerDependencies": {
@@ -5,7 +5,7 @@ import type { MMKVPlatformContext } from '../specs/MMKVPlatformContext.nitro'
5
5
  import { Platform } from 'react-native'
6
6
  import { addMemoryWarningListener } from '../addMemoryWarningListener/addMemoryWarningListener'
7
7
  import { isTest } from '../isTest'
8
- import { createMockMMKV } from './createMMKV.mock'
8
+ import { createMockMMKV } from './createMockMMKV'
9
9
 
10
10
  let factory: MMKVFactory | undefined
11
11
  let platformContext: MMKVPlatformContext | undefined
@@ -13,7 +13,7 @@ let platformContext: MMKVPlatformContext | undefined
13
13
  export function createMMKV(configuration?: Configuration): MMKV {
14
14
  if (isTest()) {
15
15
  // In a test environment, we mock the MMKV instance.
16
- return createMockMMKV()
16
+ return createMockMMKV(configuration)
17
17
  }
18
18
 
19
19
  if (platformContext == null) {
@@ -19,7 +19,9 @@ const hasAccessToLocalStorage = () => {
19
19
  const KEY_WILDCARD = '\\'
20
20
  const inMemoryStorage = new Map<string, string>()
21
21
 
22
- export function createMMKV(config: Configuration): MMKV {
22
+ export function createMMKV(
23
+ config: Configuration = { id: 'mmkv.default' }
24
+ ): MMKV {
23
25
  if (config.encryptionKey != null) {
24
26
  throw new Error("MMKV: 'encryptionKey' is not supported on Web!")
25
27
  }
@@ -78,18 +80,33 @@ export function createMMKV(config: Configuration): MMKV {
78
80
  return `${keyPrefix}${key}`
79
81
  }
80
82
 
83
+ const callListeners = (key: string) => {
84
+ listeners.forEach((l) => l(key))
85
+ }
86
+
81
87
  return {
88
+ id: config.id,
89
+ size: 0,
90
+ isReadOnly: false,
82
91
  clearAll: () => {
83
92
  const keys = Object.keys(storage())
84
93
  for (const key of keys) {
85
94
  if (key.startsWith(keyPrefix)) {
86
95
  storage().removeItem(key)
96
+ callListeners(key)
87
97
  }
88
98
  }
89
99
  },
90
- remove: (key) => storage().removeItem(prefixedKey(key)) ?? false,
100
+ remove: (key) => {
101
+ storage().removeItem(prefixedKey(key))
102
+ const wasRemoved = storage().getItem(prefixedKey(key)) === null
103
+ if (wasRemoved) callListeners(key)
104
+ return wasRemoved
105
+ },
91
106
  set: (key, value) => {
107
+ if (key === '') throw new Error('Cannot set a value for an empty key!')
92
108
  storage().setItem(prefixedKey(key), value.toString())
109
+ callListeners(key)
93
110
  },
94
111
  getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
95
112
  getNumber: (key) => {
@@ -117,8 +134,6 @@ export function createMMKV(config: Configuration): MMKV {
117
134
  recrypt: () => {
118
135
  throw new Error('`recrypt(..)` is not supported on Web!')
119
136
  },
120
- size: 0,
121
- isReadOnly: false,
122
137
  trim: () => {
123
138
  // no-op
124
139
  },
@@ -1,9 +1,12 @@
1
1
  import type { MMKV } from '../specs/MMKV.nitro'
2
+ import type { Configuration } from '../specs/MMKVFactory.nitro'
2
3
 
3
4
  /**
4
5
  * Mock MMKV instance when used in a Jest/Test environment.
5
6
  */
6
- export function createMockMMKV(): MMKV {
7
+ export function createMockMMKV(
8
+ config: Configuration = { id: 'mmkv.default' }
9
+ ): MMKV {
7
10
  const storage = new Map<string, string | boolean | number | ArrayBuffer>()
8
11
  const listeners = new Set<(key: string) => void>()
9
12
 
@@ -14,6 +17,11 @@ export function createMockMMKV(): MMKV {
14
17
  }
15
18
 
16
19
  return {
20
+ id: config.id,
21
+ get size(): number {
22
+ return storage.size
23
+ },
24
+ isReadOnly: false,
17
25
  clearAll: () => {
18
26
  const keysBefore = storage.keys()
19
27
  storage.clear()
@@ -30,6 +38,7 @@ export function createMockMMKV(): MMKV {
30
38
  return deleted
31
39
  },
32
40
  set: (key, value) => {
41
+ if (key === '') throw new Error('Cannot set a value for an empty key!')
33
42
  storage.set(key, value)
34
43
  notifyListeners(key)
35
44
  },
@@ -54,10 +63,6 @@ export function createMockMMKV(): MMKV {
54
63
  recrypt: () => {
55
64
  console.warn('Encryption is not supported in mocked MMKV instances!')
56
65
  },
57
- get size(): number {
58
- return storage.size
59
- },
60
- isReadOnly: false,
61
66
  trim: () => {
62
67
  // no-op
63
68
  },
@@ -6,9 +6,23 @@ export interface Listener {
6
6
 
7
7
  export interface MMKV extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
8
8
  /**
9
- * Set a value for the given `key`.
9
+ * Get the ID of this {@linkcode MMKV} instance.
10
+ */
11
+ readonly id: string
12
+ /**
13
+ * Get the current total size of the storage, in bytes.
14
+ */
15
+ readonly size: number
16
+ /**
17
+ * Returns whether this instance is in read-only mode or not.
18
+ * If this is `true`, you can only use "get"-functions.
19
+ */
20
+ readonly isReadOnly: boolean
21
+ /**
22
+ * Set a {@linkcode value} for the given {@linkcode key}.
10
23
  *
11
- * @throws an Error if the value cannot be set.
24
+ * @throws an Error if the {@linkcode key} is empty.
25
+ * @throws an Error if the {@linkcode value} cannot be set.
12
26
  */
13
27
  set(key: string, value: boolean | string | number | ArrayBuffer): void
14
28
  /**
@@ -74,15 +88,6 @@ export interface MMKV extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
74
88
  * In most applications, this is not needed at all.
75
89
  */
76
90
  trim(): void
77
- /**
78
- * Get the current total size of the storage, in bytes.
79
- */
80
- readonly size: number
81
- /**
82
- * Returns whether this instance is in read-only mode or not.
83
- * If this is `true`, you can only use "get"-functions.
84
- */
85
- readonly isReadOnly: boolean
86
91
  /**
87
92
  * Adds a value changed listener. The Listener will be called whenever any value
88
93
  * in this storage instance changes (set or delete).
package/app.plugin.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('./lib/expo-plugin/withMMKV.cjs')
@@ -1,5 +0,0 @@
1
- import type { MMKV } from '../specs/MMKV.nitro';
2
- /**
3
- * Mock MMKV instance when used in a Jest/Test environment.
4
- */
5
- export declare function createMockMMKV(): MMKV;
@@ -1,26 +0,0 @@
1
- "use strict";
2
- const { createRunOncePlugin, withGradleProperties } = require('@expo/config-plugins');
3
- const pkg = require('../../package.json');
4
- const withMMKV = (config) => {
5
- // remove 32-bit architectures from gradle.properties
6
- return withGradleProperties(config, (cfg) => {
7
- // Define the wanted property
8
- const property = {
9
- type: 'property',
10
- key: 'reactNativeArchitectures',
11
- value: 'arm64-v8a,x86_64',
12
- };
13
- // If it exists, update its value
14
- const index = cfg.modResults.findIndex((p) => p.type === 'property' && p.key === property.key);
15
- if (index !== -1) {
16
- // Overwrite it
17
- cfg.modResults[index] = property;
18
- }
19
- else {
20
- // Append it
21
- cfg.modResults.push(property);
22
- }
23
- return cfg;
24
- });
25
- };
26
- module.exports = createRunOncePlugin(withMMKV, pkg.name, pkg.version);
@@ -1,3 +0,0 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- declare const _default: ConfigPlugin<{}>;
3
- export = _default;
@@ -1,3 +0,0 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- declare const _default: ConfigPlugin<{}>;
3
- export default _default;
@@ -1,17 +0,0 @@
1
- import { createRunOncePlugin, withGradleProperties } from '@expo/config-plugins';
2
- const pkg = require('../../package.json');
3
- const withMMKV = (config) => {
4
- // remove 32-bit architectures from gradle.properties
5
- return withGradleProperties(config, (cfg) => {
6
- // Drop any existing entry…
7
- cfg.modResults = cfg.modResults.filter((p) => !(p.type === 'property' && p.key === 'reactNativeArchitectures'));
8
- // …and force 64-bit only.
9
- cfg.modResults.push({
10
- type: 'property',
11
- key: 'reactNativeArchitectures',
12
- value: 'arm64-v8a,x86_64',
13
- });
14
- return cfg;
15
- });
16
- };
17
- export default createRunOncePlugin(withMMKV, pkg.name, pkg.version);
@@ -1,31 +0,0 @@
1
- import type { ConfigPlugin, ExportedConfigWithProps } from '@expo/config-plugins'
2
- const { createRunOncePlugin, withGradleProperties } = require('@expo/config-plugins')
3
- import type { Properties } from '@expo/config-plugins/build/android'
4
-
5
- const pkg = require('../../package.json')
6
-
7
- const withMMKV: ConfigPlugin<{}> = (config) => {
8
- // remove 32-bit architectures from gradle.properties
9
- return withGradleProperties(config, (cfg: ExportedConfigWithProps<Properties.PropertiesItem[]>) => {
10
- // Define the wanted property
11
- const property = {
12
- type: 'property',
13
- key: 'reactNativeArchitectures',
14
- value: 'arm64-v8a,x86_64',
15
- } as const
16
- // If it exists, update its value
17
- const index = cfg.modResults.findIndex(
18
- (p) => p.type === 'property' && p.key === property.key
19
- )
20
- if (index !== -1) {
21
- // Overwrite it
22
- cfg.modResults[index] = property
23
- } else {
24
- // Append it
25
- cfg.modResults.push(property)
26
- }
27
- return cfg
28
- })
29
- }
30
-
31
- export = createRunOncePlugin(withMMKV, pkg.name, pkg.version) as ConfigPlugin<{}>