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.
- package/NitroMmkv.podspec +1 -1
- package/android/build.gradle +3 -2
- package/android/src/main/cpp/cpp-adapter.cpp +2 -1
- package/cpp/HybridMMKV.cpp +55 -12
- package/cpp/HybridMMKV.hpp +5 -0
- 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 +22 -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/NitroMmkvAutolinking.swift +8 -7
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +2 -3
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +0 -1
- package/nitrogen/generated/shared/c++/Configuration.hpp +8 -1
- 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 +23 -1
package/NitroMmkv.podspec
CHANGED
|
@@ -24,7 +24,7 @@ Pod::Spec.new do |s|
|
|
|
24
24
|
|
|
25
25
|
# Add MMKV Core dependency
|
|
26
26
|
s.compiler_flags = '-x objective-c++'
|
|
27
|
-
s.dependency 'MMKVCore', '2.
|
|
27
|
+
s.dependency 'MMKVCore', '2.3.0'
|
|
28
28
|
|
|
29
29
|
# TODO: Remove when no one uses RN 0.79 anymore
|
|
30
30
|
# Add support for React Native 0.79 or below
|
package/android/build.gradle
CHANGED
|
@@ -5,7 +5,7 @@ buildscript {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
dependencies {
|
|
8
|
-
classpath "com.android.tools.build:gradle:
|
|
8
|
+
classpath "com.android.tools.build:gradle:9.0.0"
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -74,6 +74,7 @@ android {
|
|
|
74
74
|
excludes = [
|
|
75
75
|
"META-INF",
|
|
76
76
|
"META-INF/**",
|
|
77
|
+
"**/libNitroModules.so",
|
|
77
78
|
"**/libc++_shared.so",
|
|
78
79
|
"**/libfbjni.so",
|
|
79
80
|
"**/libjsi.so",
|
|
@@ -139,6 +140,6 @@ dependencies {
|
|
|
139
140
|
implementation project(":react-native-nitro-modules")
|
|
140
141
|
|
|
141
142
|
// Add a dependency on mmkv core (this ships a C++ prefab)
|
|
142
|
-
implementation "io.github.zhongwuzw:mmkv:2.
|
|
143
|
+
implementation "io.github.zhongwuzw:mmkv:2.3.0"
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "NitroMmkvOnLoad.hpp"
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
2
3
|
#include <jni.h>
|
|
3
4
|
|
|
4
5
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
5
|
-
return margelo::nitro::mmkv::
|
|
6
|
+
return facebook::jni::initialize(vm, []() { margelo::nitro::mmkv::registerAllNatives(); });
|
|
6
7
|
}
|
package/cpp/HybridMMKV.cpp
CHANGED
|
@@ -22,6 +22,8 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
|
|
|
22
22
|
|
|
23
23
|
std::string* pathPtr = path.size() > 0 ? &path : nullptr;
|
|
24
24
|
std::string* encryptionKeyPtr = encryptionKey.size() > 0 ? &encryptionKey : nullptr;
|
|
25
|
+
size_t defaultExpectedCapacity = 0;
|
|
26
|
+
bool useAes256Encryption = config.encryptionType.has_value() && config.encryptionType.value() == EncryptionType::AES_256;
|
|
25
27
|
MMKVMode mode = getMMKVMode(config);
|
|
26
28
|
if (config.readOnly.has_value() && config.readOnly.value()) {
|
|
27
29
|
Logger::log(LogLevel::Info, TAG, "Instance is read-only!");
|
|
@@ -29,9 +31,9 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
#ifdef __APPLE__
|
|
32
|
-
instance = MMKV::mmkvWithID(config.id, mode, encryptionKeyPtr, pathPtr);
|
|
34
|
+
instance = MMKV::mmkvWithID(config.id, mode, encryptionKeyPtr, pathPtr, defaultExpectedCapacity, useAes256Encryption);
|
|
33
35
|
#else
|
|
34
|
-
instance = MMKV::mmkvWithID(config.id, DEFAULT_MMAP_SIZE, mode, encryptionKeyPtr, pathPtr);
|
|
36
|
+
instance = MMKV::mmkvWithID(config.id, DEFAULT_MMAP_SIZE, mode, encryptionKeyPtr, pathPtr, defaultExpectedCapacity, useAes256Encryption);
|
|
35
37
|
#endif
|
|
36
38
|
|
|
37
39
|
if (instance == nullptr) [[unlikely]] {
|
|
@@ -40,10 +42,18 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
|
|
|
40
42
|
throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!");
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if (useAes256Encryption) {
|
|
46
|
+
// With AES-256, the max key length is 32 bytes.
|
|
47
|
+
if (encryptionKey.size() > 32) [[unlikely]] {
|
|
48
|
+
throw std::runtime_error("Failed to create MMKV instance! `encryptionKey` cannot be longer "
|
|
49
|
+
"than 32 bytes with AES-256 encryption!");
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// With AES-128, the max key length is 16 bytes.
|
|
53
|
+
if (encryptionKey.size() > 16) [[unlikely]] {
|
|
54
|
+
throw std::runtime_error("Failed to create MMKV instance! `encryptionKey` cannot be longer "
|
|
55
|
+
"than 16 bytes with AES-128 encryption!");
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
// Check if path is maybe invalid
|
|
@@ -58,13 +68,27 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
|
|
|
58
68
|
std::string HybridMMKV::getId() {
|
|
59
69
|
return instance->mmapID();
|
|
60
70
|
}
|
|
71
|
+
|
|
72
|
+
double HybridMMKV::getLength() {
|
|
73
|
+
return instance->count();
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
double HybridMMKV::getSize() {
|
|
77
|
+
return getByteSize();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
double HybridMMKV::getByteSize() {
|
|
62
81
|
return instance->actualSize();
|
|
63
82
|
}
|
|
83
|
+
|
|
64
84
|
bool HybridMMKV::getIsReadOnly() {
|
|
65
85
|
return instance->isReadOnly();
|
|
66
86
|
}
|
|
67
87
|
|
|
88
|
+
bool HybridMMKV::getIsEncrypted() {
|
|
89
|
+
return instance->isEncryptionEnabled();
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
// helper: overload pattern matching for lambdas
|
|
69
93
|
template <class... Ts>
|
|
70
94
|
struct overloaded : Ts... {
|
|
@@ -104,6 +128,7 @@ void HybridMMKV::set(const std::string& key, const std::variant<bool, std::share
|
|
|
104
128
|
// Notify on changed
|
|
105
129
|
MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
|
|
106
130
|
}
|
|
131
|
+
|
|
107
132
|
std::optional<bool> HybridMMKV::getBoolean(const std::string& key) {
|
|
108
133
|
bool hasValue;
|
|
109
134
|
bool result = instance->getBool(key, /* defaultValue */ false, &hasValue);
|
|
@@ -113,6 +138,7 @@ std::optional<bool> HybridMMKV::getBoolean(const std::string& key) {
|
|
|
113
138
|
return std::nullopt;
|
|
114
139
|
}
|
|
115
140
|
}
|
|
141
|
+
|
|
116
142
|
std::optional<std::string> HybridMMKV::getString(const std::string& key) {
|
|
117
143
|
std::string result;
|
|
118
144
|
bool hasValue = instance->getString(key, result, /* inplaceModification */ true);
|
|
@@ -122,6 +148,7 @@ std::optional<std::string> HybridMMKV::getString(const std::string& key) {
|
|
|
122
148
|
return std::nullopt;
|
|
123
149
|
}
|
|
124
150
|
}
|
|
151
|
+
|
|
125
152
|
std::optional<double> HybridMMKV::getNumber(const std::string& key) {
|
|
126
153
|
bool hasValue;
|
|
127
154
|
double result = instance->getDouble(key, /* defaultValue */ 0.0, &hasValue);
|
|
@@ -131,6 +158,7 @@ std::optional<double> HybridMMKV::getNumber(const std::string& key) {
|
|
|
131
158
|
return std::nullopt;
|
|
132
159
|
}
|
|
133
160
|
}
|
|
161
|
+
|
|
134
162
|
std::optional<std::shared_ptr<ArrayBuffer>> HybridMMKV::getBuffer(const std::string& key) {
|
|
135
163
|
MMBuffer result;
|
|
136
164
|
#ifdef __APPLE__
|
|
@@ -147,9 +175,11 @@ std::optional<std::shared_ptr<ArrayBuffer>> HybridMMKV::getBuffer(const std::str
|
|
|
147
175
|
return std::nullopt;
|
|
148
176
|
}
|
|
149
177
|
}
|
|
178
|
+
|
|
150
179
|
bool HybridMMKV::contains(const std::string& key) {
|
|
151
180
|
return instance->containsKey(key);
|
|
152
181
|
}
|
|
182
|
+
|
|
153
183
|
bool HybridMMKV::remove(const std::string& key) {
|
|
154
184
|
bool wasRemoved = instance->removeValueForKey(key);
|
|
155
185
|
if (wasRemoved) {
|
|
@@ -158,9 +188,11 @@ bool HybridMMKV::remove(const std::string& key) {
|
|
|
158
188
|
}
|
|
159
189
|
return wasRemoved;
|
|
160
190
|
}
|
|
191
|
+
|
|
161
192
|
std::vector<std::string> HybridMMKV::getAllKeys() {
|
|
162
193
|
return instance->allKeys();
|
|
163
194
|
}
|
|
195
|
+
|
|
164
196
|
void HybridMMKV::clearAll() {
|
|
165
197
|
auto keysBefore = getAllKeys();
|
|
166
198
|
instance->clearAll();
|
|
@@ -169,19 +201,30 @@ void HybridMMKV::clearAll() {
|
|
|
169
201
|
MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
|
|
170
202
|
}
|
|
171
203
|
}
|
|
204
|
+
|
|
172
205
|
void HybridMMKV::recrypt(const std::optional<std::string>& key) {
|
|
173
|
-
bool successful = false;
|
|
174
206
|
if (key.has_value()) {
|
|
175
|
-
|
|
176
|
-
successful = instance->reKey(key.value());
|
|
207
|
+
encrypt(key.value(), std::nullopt);
|
|
177
208
|
} else {
|
|
178
|
-
|
|
179
|
-
|
|
209
|
+
decrypt();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void HybridMMKV::encrypt(const std::string& key, std::optional<EncryptionType> encryptionType) {
|
|
214
|
+
bool isAes256Encryption = encryptionType == EncryptionType::AES_256;
|
|
215
|
+
bool successful = instance->reKey(key, isAes256Encryption);
|
|
216
|
+
if (!successful) {
|
|
217
|
+
throw std::runtime_error("Failed to encrypt MMKV instance!");
|
|
180
218
|
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
void HybridMMKV::decrypt() {
|
|
222
|
+
bool successful = instance->reKey("");
|
|
181
223
|
if (!successful) {
|
|
182
|
-
throw std::runtime_error("Failed to
|
|
224
|
+
throw std::runtime_error("Failed to decrypt MMKV instance!");
|
|
183
225
|
}
|
|
184
226
|
}
|
|
227
|
+
|
|
185
228
|
void HybridMMKV::trim() {
|
|
186
229
|
instance->trim();
|
|
187
230
|
instance->clearMemoryCache();
|
package/cpp/HybridMMKV.hpp
CHANGED
|
@@ -21,7 +21,10 @@ public:
|
|
|
21
21
|
// Properties
|
|
22
22
|
std::string getId() override;
|
|
23
23
|
double getSize() override;
|
|
24
|
+
double getByteSize() override;
|
|
25
|
+
double getLength() override;
|
|
24
26
|
bool getIsReadOnly() override;
|
|
27
|
+
bool getIsEncrypted() override;
|
|
25
28
|
|
|
26
29
|
public:
|
|
27
30
|
// Methods
|
|
@@ -35,6 +38,8 @@ public:
|
|
|
35
38
|
std::vector<std::string> getAllKeys() override;
|
|
36
39
|
void clearAll() override;
|
|
37
40
|
void recrypt(const std::optional<std::string>& key) override;
|
|
41
|
+
void encrypt(const std::string& key, std::optional<EncryptionType> encryptionType) override;
|
|
42
|
+
void decrypt() override;
|
|
38
43
|
void trim() override;
|
|
39
44
|
Listener addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) override;
|
|
40
45
|
double importAllFrom(const std::shared_ptr<HybridMMKVSpec>& other) override;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Button, Text } from 'react-native';
|
|
3
|
-
import { act, fireEvent, render, renderHook, screen, cleanup, } from '@testing-library/react-native';
|
|
3
|
+
import { act, fireEvent, render, renderHook, screen, cleanup, waitFor, } from '@testing-library/react-native';
|
|
4
4
|
import { createMMKV, useMMKVNumber, useMMKVString } from '..';
|
|
5
5
|
const mmkv = createMMKV();
|
|
6
6
|
beforeEach(() => {
|
|
@@ -67,3 +67,32 @@ test('functional updates to hooks', () => {
|
|
|
67
67
|
'4',
|
|
68
68
|
]);
|
|
69
69
|
});
|
|
70
|
+
test('useMMKV hook does not miss updates that happen during subscription setup', async () => {
|
|
71
|
+
const raceKey = 'race-key';
|
|
72
|
+
const raceMMKV = createMMKV();
|
|
73
|
+
let simulatedRaceDone = false;
|
|
74
|
+
const originalSubscribe = raceMMKV.addOnValueChangedListener.bind(raceMMKV);
|
|
75
|
+
raceMMKV.addOnValueChangedListener = ((listener) => {
|
|
76
|
+
if (!simulatedRaceDone) {
|
|
77
|
+
simulatedRaceDone = true;
|
|
78
|
+
raceMMKV.set(raceKey, 'updated-before-subscribe');
|
|
79
|
+
}
|
|
80
|
+
return originalSubscribe(listener);
|
|
81
|
+
});
|
|
82
|
+
const { result } = renderHook(() => useMMKVString(raceKey, raceMMKV));
|
|
83
|
+
await waitFor(() => {
|
|
84
|
+
expect(result.current[0]).toBe('updated-before-subscribe');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
test('useMMKV hook stays consistent during rapid updates', async () => {
|
|
88
|
+
const raceKey = 'rapid-key';
|
|
89
|
+
const { result } = renderHook(() => useMMKVNumber(raceKey, mmkv));
|
|
90
|
+
act(() => {
|
|
91
|
+
for (let i = 1; i <= 100; i++) {
|
|
92
|
+
mmkv.set(raceKey, i);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
await waitFor(() => {
|
|
96
|
+
expect(result.current[0]).toBe(100);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -24,8 +24,18 @@ export function createMMKV(config = { id: 'mmkv.default' }) {
|
|
|
24
24
|
};
|
|
25
25
|
return {
|
|
26
26
|
id: config.id,
|
|
27
|
-
|
|
27
|
+
get length() {
|
|
28
|
+
return getLocalStorage().length;
|
|
29
|
+
},
|
|
30
|
+
get size() {
|
|
31
|
+
return this.byteSize;
|
|
32
|
+
},
|
|
33
|
+
get byteSize() {
|
|
34
|
+
// esimate - assumes UTF8
|
|
35
|
+
return JSON.stringify(getLocalStorage()).length;
|
|
36
|
+
},
|
|
28
37
|
isReadOnly: false,
|
|
38
|
+
isEncrypted: false,
|
|
29
39
|
clearAll: () => {
|
|
30
40
|
const storage = getLocalStorage();
|
|
31
41
|
const keys = Object.keys(storage);
|
|
@@ -90,6 +100,12 @@ export function createMMKV(config = { id: 'mmkv.default' }) {
|
|
|
90
100
|
recrypt: () => {
|
|
91
101
|
throw new Error('`recrypt(..)` is not supported on Web!');
|
|
92
102
|
},
|
|
103
|
+
encrypt: () => {
|
|
104
|
+
throw new Error('`encrypt(..)` is not supported on Web!');
|
|
105
|
+
},
|
|
106
|
+
decrypt: () => {
|
|
107
|
+
throw new Error('`decrypt(..)` is not supported on Web!');
|
|
108
|
+
},
|
|
93
109
|
trim: () => {
|
|
94
110
|
// no-op
|
|
95
111
|
},
|
|
@@ -11,10 +11,18 @@ export function createMockMMKV(config = { id: 'mmkv.default' }) {
|
|
|
11
11
|
};
|
|
12
12
|
return {
|
|
13
13
|
id: config.id,
|
|
14
|
-
get
|
|
14
|
+
get length() {
|
|
15
15
|
return storage.size;
|
|
16
16
|
},
|
|
17
|
+
get size() {
|
|
18
|
+
return this.byteSize;
|
|
19
|
+
},
|
|
20
|
+
get byteSize() {
|
|
21
|
+
// esimate - assumes UTF8
|
|
22
|
+
return JSON.stringify(storage).length;
|
|
23
|
+
},
|
|
17
24
|
isReadOnly: false,
|
|
25
|
+
isEncrypted: false,
|
|
18
26
|
clearAll: () => {
|
|
19
27
|
const keysBefore = storage.keys();
|
|
20
28
|
storage.clear();
|
|
@@ -57,6 +65,12 @@ export function createMockMMKV(config = { id: 'mmkv.default' }) {
|
|
|
57
65
|
recrypt: () => {
|
|
58
66
|
console.warn('Encryption is not supported in mocked MMKV instances!');
|
|
59
67
|
},
|
|
68
|
+
encrypt: () => {
|
|
69
|
+
console.warn('Encryption is not supported in mocked MMKV instances!');
|
|
70
|
+
},
|
|
71
|
+
decrypt: () => {
|
|
72
|
+
console.warn('Encryption is not supported in mocked MMKV instances!');
|
|
73
|
+
},
|
|
60
74
|
trim: () => {
|
|
61
75
|
// no-op
|
|
62
76
|
},
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
2
2
|
import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance';
|
|
3
3
|
export function createMMKVHook(getter) {
|
|
4
4
|
return (key, instance) => {
|
|
5
5
|
const mmkv = instance ?? getDefaultMMKVInstance();
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
}, [mmkv, key,
|
|
6
|
+
const value = useSyncExternalStore(useCallback((onStoreChange) => {
|
|
7
|
+
const listener = mmkv.addOnValueChangedListener((changedKey) => {
|
|
8
|
+
if (changedKey === key) {
|
|
9
|
+
onStoreChange();
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return () => listener.remove();
|
|
13
|
+
}, [key, mmkv]), useCallback(() => getter(mmkv, key), [key, mmkv]), useCallback(() => getter(mmkv, key), [key, mmkv]));
|
|
14
14
|
// update value by user set
|
|
15
15
|
const set = useCallback((v) => {
|
|
16
16
|
const newValue = typeof v === 'function' ? v(getter(mmkv, key)) : v;
|
|
@@ -35,15 +35,6 @@ export function createMMKVHook(getter) {
|
|
|
35
35
|
throw new Error(`MMKV: Type ${typeof newValue} is not supported!`);
|
|
36
36
|
}
|
|
37
37
|
}, [key, mmkv]);
|
|
38
|
-
// update value if it changes somewhere else (second hook, same key)
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
const listener = mmkv.addOnValueChangedListener((changedKey) => {
|
|
41
|
-
if (changedKey === key) {
|
|
42
|
-
setBump((b) => b + 1);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return () => listener.remove();
|
|
46
|
-
}, [key, mmkv]);
|
|
47
38
|
return [value, set];
|
|
48
39
|
};
|
|
49
40
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { EncryptionType } from './MMKVFactory.nitro';
|
|
2
3
|
export interface Listener {
|
|
3
4
|
remove: () => void;
|
|
4
5
|
}
|
|
@@ -10,15 +11,31 @@ export interface MMKV extends HybridObject<{
|
|
|
10
11
|
* Get the ID of this {@linkcode MMKV} instance.
|
|
11
12
|
*/
|
|
12
13
|
readonly id: string;
|
|
14
|
+
/**
|
|
15
|
+
* Get the current amount of key/value pairs stored in
|
|
16
|
+
* this storage.
|
|
17
|
+
*/
|
|
18
|
+
readonly length: number;
|
|
13
19
|
/**
|
|
14
20
|
* Get the current total size of the storage, in bytes.
|
|
21
|
+
* @deprecated Use {@linkcode byteSize} instead.
|
|
15
22
|
*/
|
|
16
23
|
readonly size: number;
|
|
17
24
|
/**
|
|
18
|
-
*
|
|
25
|
+
* Get the current total size of the storage, in bytes.
|
|
26
|
+
*/
|
|
27
|
+
readonly byteSize: number;
|
|
28
|
+
/**
|
|
29
|
+
* Get whether this instance is in read-only mode or not.
|
|
19
30
|
* If this is `true`, you can only use "get"-functions.
|
|
20
31
|
*/
|
|
21
32
|
readonly isReadOnly: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get whether this instance is encrypted, or not.
|
|
35
|
+
* @see {@linkcode encrypt | encrypt(...)}
|
|
36
|
+
* @see {@linkcode decrypt | decrypt()}
|
|
37
|
+
*/
|
|
38
|
+
readonly isEncrypted: boolean;
|
|
22
39
|
/**
|
|
23
40
|
* Set a {@linkcode value} for the given {@linkcode key}.
|
|
24
41
|
*
|
|
@@ -77,8 +94,36 @@ export interface MMKV extends HybridObject<{
|
|
|
77
94
|
* Encryption keys can have a maximum length of 16 bytes.
|
|
78
95
|
*
|
|
79
96
|
* @throws an Error if the instance cannot be recrypted.
|
|
97
|
+
* @deprecated Use {@linkcode encrypt | encrypt(...)} or {@linkcode decrypt | decrypt()} instead.
|
|
80
98
|
*/
|
|
81
99
|
recrypt(key: string | undefined): void;
|
|
100
|
+
/**
|
|
101
|
+
* Encrypts the data in this MMKV instance with the
|
|
102
|
+
* given {@linkcode key} and {@linkcode ecnryptionType}.
|
|
103
|
+
*
|
|
104
|
+
* If this MMKV instance is already encrypted ({@linkcode isEncrypted}),
|
|
105
|
+
* it will re-encrypt the data.
|
|
106
|
+
*
|
|
107
|
+
* Future attempts to open this MMKV instance will require the same
|
|
108
|
+
* {@linkcode key} and {@linkcode encryptionType}, otherwise reads
|
|
109
|
+
* will fail.
|
|
110
|
+
*
|
|
111
|
+
* The {@linkcode key} must be 16-bytes in {@linkcode EncryptionType | 'AES-128'}
|
|
112
|
+
* encryption (the default), or 32-bytes in {@linkcode EncryptionType | 'AES-256'}
|
|
113
|
+
* encryption.
|
|
114
|
+
*
|
|
115
|
+
* @param key The encryption key to use. In AES-128 this must be 16-bytes, in AES-256 it must be 32-bytes long.
|
|
116
|
+
* @param encryptionType The encryption type to use. Default: AES-128
|
|
117
|
+
*/
|
|
118
|
+
encrypt(key: string, encryptionType?: EncryptionType): void;
|
|
119
|
+
/**
|
|
120
|
+
* Decrypts the data in this MMKV instance and removes
|
|
121
|
+
* the encryption key.
|
|
122
|
+
*
|
|
123
|
+
* Future attempts to open this MMKV instance must be done
|
|
124
|
+
* without an encryption key, as it is now plain-text.
|
|
125
|
+
*/
|
|
126
|
+
decrypt(): void;
|
|
82
127
|
/**
|
|
83
128
|
* Trims the storage space and clears memory cache.
|
|
84
129
|
*
|
|
@@ -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
|
*
|
|
@@ -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
|