react-native-mmkv 4.0.0-beta.3 → 4.0.0-beta.5

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.
@@ -0,0 +1,184 @@
1
+ //
2
+ // HybridMMKV.cpp
3
+ // react-native-mmkv
4
+ //
5
+ // Created by Marc Rousavy on 21.08.2025.
6
+ //
7
+
8
+ #include "HybridMMKV.hpp"
9
+ #include "MMKVTypes.hpp"
10
+ #include "MMKVValueChangedListenerRegistry.hpp"
11
+ #include "ManagedMMBuffer.hpp"
12
+ #include <NitroModules/NitroLogger.hpp>
13
+
14
+ namespace margelo::nitro::mmkv {
15
+
16
+ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
17
+ std::string path = config.path.has_value() ? config.path.value() : "";
18
+ std::string encryptionKey = config.encryptionKey.has_value() ? config.encryptionKey.value() : "";
19
+ bool hasEncryptionKey = encryptionKey.size() > 0;
20
+ Logger::log(LogLevel::Info, TAG, "Creating MMKV instance \"%s\"... (Path: %s, Encrypted: %s)", config.id.c_str(), path.c_str(),
21
+ hasEncryptionKey ? "true" : "false");
22
+
23
+ std::string* pathPtr = path.size() > 0 ? &path : nullptr;
24
+ std::string* encryptionKeyPtr = encryptionKey.size() > 0 ? &encryptionKey : nullptr;
25
+ MMKVMode mode = getMMKVMode(config);
26
+ if (config.readOnly.has_value() && config.readOnly.value()) {
27
+ Logger::log(LogLevel::Info, TAG, "Instance is read-only!");
28
+ mode = mode | ::mmkv::MMKV_READ_ONLY;
29
+ }
30
+
31
+ #ifdef __APPLE__
32
+ instance = MMKV::mmkvWithID(config.id, mode, encryptionKeyPtr, pathPtr);
33
+ #else
34
+ instance = MMKV::mmkvWithID(config.id, DEFAULT_MMAP_SIZE, mode, encryptionKeyPtr, pathPtr);
35
+ #endif
36
+
37
+ if (instance == nullptr) [[unlikely]] {
38
+ // Check if instanceId is invalid
39
+ if (config.id.empty()) [[unlikely]] {
40
+ throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!");
41
+ }
42
+
43
+ // Check if encryptionKey is invalid
44
+ if (encryptionKey.size() > 16) [[unlikely]] {
45
+ throw std::runtime_error("Failed to create MMKV instance! `encryptionKey` cannot be longer "
46
+ "than 16 bytes!");
47
+ }
48
+
49
+ // Check if path is maybe invalid
50
+ if (path.empty()) [[unlikely]] {
51
+ throw std::runtime_error("Failed to create MMKV instance! `path` cannot be empty!");
52
+ }
53
+
54
+ throw std::runtime_error("Failed to create MMKV instance!");
55
+ }
56
+ }
57
+
58
+ double HybridMMKV::getSize() {
59
+ return instance->actualSize();
60
+ }
61
+ bool HybridMMKV::getIsReadOnly() {
62
+ return instance->isReadOnly();
63
+ }
64
+
65
+ // helper: overload pattern matching for lambdas
66
+ template <class... Ts>
67
+ struct overloaded : Ts... {
68
+ using Ts::operator()...;
69
+ };
70
+ template <class... Ts>
71
+ overloaded(Ts...) -> overloaded<Ts...>;
72
+
73
+ void HybridMMKV::set(const std::string& key, const std::variant<std::string, double, bool, std::shared_ptr<ArrayBuffer>>& value) {
74
+ // 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
+
83
+ // Notify on changed
84
+ MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
85
+ }
86
+ std::optional<bool> HybridMMKV::getBoolean(const std::string& key) {
87
+ bool hasValue;
88
+ bool result = instance->getBool(key, /* defaultValue */ false, &hasValue);
89
+ if (hasValue) {
90
+ return result;
91
+ } else {
92
+ return std::nullopt;
93
+ }
94
+ }
95
+ std::optional<std::string> HybridMMKV::getString(const std::string& key) {
96
+ std::string result;
97
+ bool hasValue = instance->getString(key, result, /* inplaceModification */ true);
98
+ if (hasValue) {
99
+ return result;
100
+ } else {
101
+ return std::nullopt;
102
+ }
103
+ }
104
+ std::optional<double> HybridMMKV::getNumber(const std::string& key) {
105
+ bool hasValue;
106
+ double result = instance->getDouble(key, /* defaultValue */ 0.0, &hasValue);
107
+ if (hasValue) {
108
+ return result;
109
+ } else {
110
+ return std::nullopt;
111
+ }
112
+ }
113
+ std::optional<std::shared_ptr<ArrayBuffer>> HybridMMKV::getBuffer(const std::string& key) {
114
+ MMBuffer result;
115
+ #ifdef __APPLE__
116
+ // iOS: Convert std::string to NSString* for MMKVCore pod compatibility
117
+ bool hasValue = instance->getBytes(@(key.c_str()), result);
118
+ #else
119
+ // Android/other platforms: Use std::string directly (converts to
120
+ // std::string_view)
121
+ bool hasValue = instance->getBytes(key, result);
122
+ #endif
123
+ if (hasValue) {
124
+ return std::make_shared<ManagedMMBuffer>(std::move(result));
125
+ } else {
126
+ return std::nullopt;
127
+ }
128
+ }
129
+ bool HybridMMKV::contains(const std::string& key) {
130
+ return instance->containsKey(key);
131
+ }
132
+ void HybridMMKV::remove(const std::string& key) {
133
+ instance->removeValueForKey(key);
134
+ }
135
+ std::vector<std::string> HybridMMKV::getAllKeys() {
136
+ return instance->allKeys();
137
+ }
138
+ void HybridMMKV::clearAll() {
139
+ instance->clearAll();
140
+ }
141
+ void HybridMMKV::recrypt(const std::optional<std::string>& key) {
142
+ bool successful = false;
143
+ if (key.has_value()) {
144
+ // Encrypt with the given key
145
+ successful = instance->reKey(key.value());
146
+ } else {
147
+ // Remove the encryption key by setting it to a blank string
148
+ successful = instance->reKey(std::string());
149
+ }
150
+ if (!successful) {
151
+ throw std::runtime_error("Failed to recrypt MMKV instance!");
152
+ }
153
+ }
154
+ void HybridMMKV::trim() {
155
+ instance->trim();
156
+ instance->clearMemoryCache();
157
+ }
158
+
159
+ Listener HybridMMKV::addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) {
160
+ // Add listener
161
+ auto mmkvID = instance->mmapID();
162
+ auto listenerID = MMKVValueChangedListenerRegistry::addListener(instance->mmapID(), onValueChanged);
163
+
164
+ return Listener([=]() {
165
+ // remove()
166
+ MMKVValueChangedListenerRegistry::removeListener(mmkvID, listenerID);
167
+ });
168
+ }
169
+
170
+ MMKVMode HybridMMKV::getMMKVMode(const Configuration& config) {
171
+ if (!config.mode.has_value()) {
172
+ return ::mmkv::MMKV_SINGLE_PROCESS;
173
+ }
174
+ switch (config.mode.value()) {
175
+ case Mode::SINGLE_PROCESS:
176
+ return ::mmkv::MMKV_SINGLE_PROCESS;
177
+ case Mode::MULTI_PROCESS:
178
+ return ::mmkv::MMKV_MULTI_PROCESS;
179
+ default:
180
+ [[unlikely]] throw std::runtime_error("Invalid MMKV Mode value!");
181
+ }
182
+ }
183
+
184
+ } // namespace margelo::nitro::mmkv
@@ -0,0 +1,33 @@
1
+ //
2
+ // HybridMMKVFactory.cpp
3
+ // react-native-mmkv
4
+ //
5
+ // Created by Marc Rousavy on 21.08.2025.
6
+ //
7
+
8
+ #include "HybridMMKVFactory.hpp"
9
+ #include "HybridMMKV.hpp"
10
+ #include "MMKVTypes.hpp"
11
+
12
+ namespace margelo::nitro::mmkv {
13
+
14
+ std::string HybridMMKVFactory::getDefaultMMKVInstanceId() {
15
+ return DEFAULT_MMAP_ID;
16
+ }
17
+
18
+ std::shared_ptr<HybridMMKVSpec> HybridMMKVFactory::createMMKV(const Configuration& configuration) {
19
+ return std::make_shared<HybridMMKV>(configuration);
20
+ }
21
+
22
+ void HybridMMKVFactory::initializeMMKV(const std::string& rootPath) {
23
+ Logger::log(LogLevel::Info, TAG, "Initializing MMKV with rootPath=%s", rootPath.c_str());
24
+
25
+ #ifdef NITRO_DEBUG
26
+ MMKVLogLevel logLevel = ::mmkv::MMKVLogDebug;
27
+ #else
28
+ MMKVLogLevel logLevel = ::mmkv::MMKVLogWarning;
29
+ #endif
30
+ MMKV::initializeMMKV(rootPath, logLevel);
31
+ }
32
+
33
+ } // namespace margelo::nitro::mmkv
@@ -0,0 +1,58 @@
1
+ //
2
+ // MMKVValueChangedListenerRegistry.cpp
3
+ // react-native-mmkv
4
+ //
5
+ // Created by Marc Rousavy on 21.08.2025.
6
+ //
7
+
8
+ #include "MMKVValueChangedListenerRegistry.hpp"
9
+
10
+ namespace margelo::nitro::mmkv {
11
+
12
+ // static members
13
+ std::atomic<ListenerID> MMKVValueChangedListenerRegistry::_listenersCounter = 0;
14
+ std::unordered_map<MMKVID, std::vector<ListenerSubscription>> MMKVValueChangedListenerRegistry::_listeners;
15
+
16
+ ListenerID MMKVValueChangedListenerRegistry::addListener(const std::string& mmkvID,
17
+ const std::function<void(const std::string& /* key */)>& callback) {
18
+ // 1. Get (or create) the listeners array for these MMKV instances
19
+ auto& listeners = _listeners[mmkvID];
20
+ // 2. Get (and increment) the listener ID counter
21
+ auto id = _listenersCounter.fetch_add(1);
22
+ // 3. Add the listener to our array
23
+ listeners.push_back(ListenerSubscription{
24
+ .id = id,
25
+ .callback = callback,
26
+ });
27
+ // 4. Return the ID used to unsubscribe later on
28
+ return id;
29
+ }
30
+
31
+ void MMKVValueChangedListenerRegistry::removeListener(const std::string& mmkvID, ListenerID id) {
32
+ // 1. Get the listeners array for these MMKV instances
33
+ auto entry = _listeners.find(mmkvID);
34
+ if (entry == _listeners.end()) {
35
+ // There's no more listeners for this instance anyways.
36
+ return;
37
+ }
38
+ // 2. Remove all listeners where the ID matches. Should only be one.
39
+ auto& listeners = entry->second;
40
+ listeners.erase(std::remove_if(listeners.begin(), listeners.end(), [id](const ListenerSubscription& e) { return e.id == id; }),
41
+ listeners.end());
42
+ }
43
+
44
+ void MMKVValueChangedListenerRegistry::notifyOnValueChanged(const std::string& mmkvID, const std::string& key) {
45
+ // 1. Get all listeners for the specific MMKV ID
46
+ auto entry = _listeners.find(mmkvID);
47
+ if (entry == _listeners.end()) {
48
+ // There are no listeners. Return
49
+ return;
50
+ }
51
+ // 2. Call each listener.
52
+ auto& listeners = entry->second;
53
+ for (const auto& listener : listeners) {
54
+ listener.callback(key);
55
+ }
56
+ }
57
+
58
+ } // namespace margelo::nitro::mmkv
package/nitro.json CHANGED
@@ -23,5 +23,6 @@
23
23
  },
24
24
  "ignorePaths": [
25
25
  "**/node_modules"
26
- ]
26
+ ],
27
+ "gitAttributesGeneratedFlag": false
27
28
  }
@@ -1 +1 @@
1
- ** linguist-generated=true
1
+ ** linguist-generated=false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mmkv",
3
- "version": "4.0.0-beta.3",
3
+ "version": "4.0.0-beta.5",
4
4
  "description": "react-native-mmkv",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
@@ -22,6 +22,9 @@
22
22
  "ios/**/*.mm",
23
23
  "ios/**/*.cpp",
24
24
  "ios/**/*.swift",
25
+ "cpp/**/*.h",
26
+ "cpp/**/*.cpp",
27
+ "cpp/**/*.cpp",
25
28
  "app.plugin.js",
26
29
  "nitro.json",
27
30
  "*.podspec",