react-native-mmkv 4.3.1 → 4.3.2
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/android/build.gradle +11 -12
- package/android/gradle.properties +2 -2
- package/cpp/HybridMMKV.cpp +24 -1
- package/cpp/HybridMMKV.hpp +5 -0
- package/lib/__tests__/contentChangedListener.test.d.ts +1 -0
- package/lib/__tests__/contentChangedListener.test.js +46 -0
- package/lib/addContentChangedListener/addContentChangedListener.d.ts +2 -0
- package/lib/addContentChangedListener/addContentChangedListener.js +27 -0
- package/lib/addContentChangedListener/addContentChangedListener.mock.d.ts +2 -0
- package/lib/addContentChangedListener/addContentChangedListener.mock.js +3 -0
- package/lib/addContentChangedListener/addContentChangedListener.web.d.ts +2 -0
- package/lib/addContentChangedListener/addContentChangedListener.web.js +3 -0
- package/lib/createMMKV/createMMKV.js +3 -0
- package/lib/createMMKV/createMMKV.web.js +5 -1
- package/lib/createMMKV/createMockMMKV.js +3 -0
- package/lib/hooks/useMMKV.js +5 -1
- package/lib/index.d.ts +1 -1
- package/lib/specs/MMKV.nitro.d.ts +5 -0
- package/lib/specs/MMKVFactory.nitro.d.ts +12 -0
- package/nitro.json +12 -3
- package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +2 -2
- package/nitrogen/generated/ios/NitroMmkv+autolinking.rb +2 -0
- package/nitrogen/generated/shared/c++/Configuration.hpp +9 -2
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/RecoveryStrategy.hpp +76 -0
- package/package.json +16 -32
- package/src/__tests__/contentChangedListener.test.ts +61 -0
- package/src/addContentChangedListener/addContentChangedListener.mock.ts +5 -0
- package/src/addContentChangedListener/addContentChangedListener.ts +36 -0
- package/src/addContentChangedListener/addContentChangedListener.web.ts +5 -0
- package/src/createMMKV/createMMKV.ts +3 -0
- package/src/createMMKV/createMMKV.web.ts +5 -1
- package/src/createMMKV/createMockMMKV.ts +3 -0
- package/src/hooks/useMMKV.ts +5 -1
- package/src/index.ts +5 -1
- package/src/specs/MMKV.nitro.ts +5 -0
- package/src/specs/MMKVFactory.nitro.ts +17 -2
- package/src/specs/MMKVPlatformContext.nitro.ts +4 -2
package/android/build.gradle
CHANGED
|
@@ -5,7 +5,7 @@ buildscript {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
dependencies {
|
|
8
|
-
classpath "com.android.tools.build:gradle:9.1
|
|
8
|
+
classpath "com.android.tools.build:gradle:9.2.1"
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -36,14 +36,14 @@ def getExtOrIntegerDefault(name) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
android {
|
|
39
|
-
namespace "com.margelo.nitro.mmkv"
|
|
39
|
+
namespace = "com.margelo.nitro.mmkv"
|
|
40
40
|
|
|
41
|
-
ndkVersion getExtOrDefault("ndkVersion")
|
|
42
|
-
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
41
|
+
ndkVersion = getExtOrDefault("ndkVersion")
|
|
42
|
+
compileSdkVersion = getExtOrIntegerDefault("compileSdkVersion")
|
|
43
43
|
|
|
44
44
|
defaultConfig {
|
|
45
|
-
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
46
|
-
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
45
|
+
minSdkVersion = getExtOrIntegerDefault("minSdkVersion")
|
|
46
|
+
targetSdkVersion = getExtOrIntegerDefault("targetSdkVersion")
|
|
47
47
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
48
48
|
|
|
49
49
|
externalNativeBuild {
|
|
@@ -71,7 +71,7 @@ android {
|
|
|
71
71
|
|
|
72
72
|
externalNativeBuild {
|
|
73
73
|
cmake {
|
|
74
|
-
path "CMakeLists.txt"
|
|
74
|
+
path = file("CMakeLists.txt")
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -98,13 +98,13 @@ android {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
buildFeatures {
|
|
101
|
-
buildConfig true
|
|
102
|
-
prefab true
|
|
101
|
+
buildConfig = true
|
|
102
|
+
prefab = true
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
buildTypes {
|
|
106
106
|
release {
|
|
107
|
-
minifyEnabled false
|
|
107
|
+
minifyEnabled = false
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -145,6 +145,5 @@ dependencies {
|
|
|
145
145
|
implementation project(":react-native-nitro-modules")
|
|
146
146
|
|
|
147
147
|
// Add a dependency on mmkv core (this ships a C++ prefab)
|
|
148
|
-
implementation "io.github.zhongwuzw:mmkv:2.4.
|
|
148
|
+
implementation "io.github.zhongwuzw:mmkv:2.4.1"
|
|
149
149
|
}
|
|
150
|
-
|
package/cpp/HybridMMKV.cpp
CHANGED
|
@@ -29,7 +29,8 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
|
|
|
29
29
|
.aes256 = useAes256Encryption,
|
|
30
30
|
.cryptKey = encryptionKeyPtr,
|
|
31
31
|
.rootPath = rootPathPtr,
|
|
32
|
-
.enableCompareBeforeSet = compareBeforeSet
|
|
32
|
+
.enableCompareBeforeSet = compareBeforeSet,
|
|
33
|
+
.recover = getRecoveryStrategy(config)};
|
|
33
34
|
|
|
34
35
|
bool hasEncryptionKey = encryptionKey.size() > 0;
|
|
35
36
|
Logger::log(LogLevel::Info, TAG, "Creating MMKV instance \"%s\"... (Path: %s, Encrypted: %s)", config.id.c_str(), rootPath.c_str(),
|
|
@@ -82,6 +83,10 @@ double HybridMMKV::getByteSize() {
|
|
|
82
83
|
return instance->actualSize();
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
size_t HybridMMKV::getExternalMemorySize() noexcept {
|
|
87
|
+
return instance != nullptr ? instance->actualSize() : 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
bool HybridMMKV::getIsReadOnly() {
|
|
86
91
|
return instance->isReadOnly();
|
|
87
92
|
}
|
|
@@ -224,6 +229,10 @@ void HybridMMKV::trim() {
|
|
|
224
229
|
instance->clearMemoryCache();
|
|
225
230
|
}
|
|
226
231
|
|
|
232
|
+
void HybridMMKV::checkContentChanged() {
|
|
233
|
+
instance->checkContentChanged();
|
|
234
|
+
}
|
|
235
|
+
|
|
227
236
|
Listener HybridMMKV::addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) {
|
|
228
237
|
// Add listener
|
|
229
238
|
auto mmkvID = instance->mmapID();
|
|
@@ -248,6 +257,20 @@ MMKVMode HybridMMKV::getMMKVMode(const Configuration& config) {
|
|
|
248
257
|
throw std::runtime_error("Invalid MMKV Mode value!");
|
|
249
258
|
}
|
|
250
259
|
|
|
260
|
+
std::optional<MMKVRecoverStrategic> HybridMMKV::getRecoveryStrategy(const Configuration& config) {
|
|
261
|
+
if (!config.recoveryStrategy.has_value()) {
|
|
262
|
+
return std::nullopt;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
switch (config.recoveryStrategy.value()) {
|
|
266
|
+
case RecoveryStrategy::DISCARD_ON_ERROR:
|
|
267
|
+
return MMKVRecoverStrategic::OnErrorDiscard;
|
|
268
|
+
case RecoveryStrategy::RECOVER_ON_ERROR:
|
|
269
|
+
return MMKVRecoverStrategic::OnErrorRecover;
|
|
270
|
+
}
|
|
271
|
+
throw std::runtime_error("Invalid MMKV RecoveryStrategy value!");
|
|
272
|
+
}
|
|
273
|
+
|
|
251
274
|
double HybridMMKV::importAllFrom(const std::shared_ptr<HybridMMKVSpec>& other) {
|
|
252
275
|
auto hybridMMKV = std::dynamic_pointer_cast<HybridMMKV>(other);
|
|
253
276
|
if (hybridMMKV == nullptr) [[unlikely]] {
|
package/cpp/HybridMMKV.hpp
CHANGED
|
@@ -41,11 +41,16 @@ public:
|
|
|
41
41
|
void encrypt(const std::string& key, std::optional<EncryptionType> encryptionType) override;
|
|
42
42
|
void decrypt() override;
|
|
43
43
|
void trim() override;
|
|
44
|
+
void checkContentChanged() override;
|
|
44
45
|
Listener addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) override;
|
|
45
46
|
double importAllFrom(const std::shared_ptr<HybridMMKVSpec>& other) override;
|
|
46
47
|
|
|
48
|
+
protected:
|
|
49
|
+
size_t getExternalMemorySize() noexcept override;
|
|
50
|
+
|
|
47
51
|
private:
|
|
48
52
|
static MMKVMode getMMKVMode(const Configuration& config);
|
|
53
|
+
static std::optional<MMKVRecoverStrategic> getRecoveryStrategy(const Configuration& config);
|
|
49
54
|
|
|
50
55
|
private:
|
|
51
56
|
MMKV* instance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AppState } from 'react-native';
|
|
2
|
+
import { createMockMMKV } from '../createMMKV/createMockMMKV';
|
|
3
|
+
import { addContentChangedListener } from '../addContentChangedListener/addContentChangedListener';
|
|
4
|
+
function mockAppStateChangeListener() {
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
jest
|
|
7
|
+
.spyOn(AppState, 'addEventListener')
|
|
8
|
+
.mockImplementation((type, listener) => {
|
|
9
|
+
if (type === 'change') {
|
|
10
|
+
listeners.add(listener);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
remove: () => {
|
|
14
|
+
listeners.delete(listener);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
emit: (state) => {
|
|
20
|
+
listeners.forEach((listener) => listener(state));
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
jest.restoreAllMocks();
|
|
27
|
+
});
|
|
28
|
+
test('content changed listener checks content when app becomes active', () => {
|
|
29
|
+
const appState = mockAppStateChangeListener();
|
|
30
|
+
const storage = createMockMMKV({ id: 'content-changed-listener-test' });
|
|
31
|
+
const checkContentChanged = jest.spyOn(storage, 'checkContentChanged');
|
|
32
|
+
addContentChangedListener(storage);
|
|
33
|
+
appState.emit('background');
|
|
34
|
+
expect(checkContentChanged).not.toHaveBeenCalled();
|
|
35
|
+
appState.emit('active');
|
|
36
|
+
expect(checkContentChanged).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
test('content changed listener registers once per instance', () => {
|
|
39
|
+
mockAppStateChangeListener();
|
|
40
|
+
const storage = createMockMMKV({
|
|
41
|
+
id: 'content-changed-listener-once-test',
|
|
42
|
+
});
|
|
43
|
+
addContentChangedListener(storage);
|
|
44
|
+
addContentChangedListener(storage);
|
|
45
|
+
expect(AppState.addEventListener).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AppState } from 'react-native';
|
|
2
|
+
const registeredInstances = new WeakSet();
|
|
3
|
+
export function addContentChangedListener(mmkv) {
|
|
4
|
+
if (registeredInstances.has(mmkv)) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
registeredInstances.add(mmkv);
|
|
8
|
+
if (global.WeakRef != null && global.FinalizationRegistry != null) {
|
|
9
|
+
const weakMmkv = new WeakRef(mmkv);
|
|
10
|
+
const listener = AppState.addEventListener('change', (state) => {
|
|
11
|
+
if (state === 'active') {
|
|
12
|
+
weakMmkv.deref()?.checkContentChanged();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const finalization = new FinalizationRegistry((l) => {
|
|
16
|
+
l.remove();
|
|
17
|
+
});
|
|
18
|
+
finalization.register(mmkv, listener);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
AppState.addEventListener('change', (state) => {
|
|
22
|
+
if (state === 'active') {
|
|
23
|
+
mmkv.checkContentChanged();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
2
|
import { addMemoryWarningListener } from '../addMemoryWarningListener/addMemoryWarningListener';
|
|
3
|
+
import { addContentChangedListener } from '../addContentChangedListener/addContentChangedListener';
|
|
3
4
|
import { isTest } from '../isTest';
|
|
4
5
|
import { createMockMMKV } from './createMockMMKV';
|
|
5
6
|
import { getMMKVFactory, getPlatformContext } from '../getMMKVFactory';
|
|
@@ -26,5 +27,7 @@ export function createMMKV(configuration) {
|
|
|
26
27
|
const mmkv = factory.createMMKV(config);
|
|
27
28
|
// Add a hook that trims the storage when we get a memory warning
|
|
28
29
|
addMemoryWarningListener(mmkv);
|
|
30
|
+
// Check for updates from app extensions/App Clips/background services when the app becomes active
|
|
31
|
+
addContentChangedListener(mmkv);
|
|
29
32
|
return mmkv;
|
|
30
33
|
}
|
|
@@ -27,7 +27,8 @@ export function createMMKV(config = { id: 'mmkv.default' }) {
|
|
|
27
27
|
return {
|
|
28
28
|
id: config.id,
|
|
29
29
|
get length() {
|
|
30
|
-
|
|
30
|
+
const keys = this.getAllKeys();
|
|
31
|
+
return keys.length;
|
|
31
32
|
},
|
|
32
33
|
get size() {
|
|
33
34
|
return this.byteSize;
|
|
@@ -116,6 +117,9 @@ export function createMMKV(config = { id: 'mmkv.default' }) {
|
|
|
116
117
|
trim: () => {
|
|
117
118
|
// no-op
|
|
118
119
|
},
|
|
120
|
+
checkContentChanged: () => {
|
|
121
|
+
// no-op
|
|
122
|
+
},
|
|
119
123
|
dispose: () => { },
|
|
120
124
|
equals: () => false,
|
|
121
125
|
name: 'MMKV',
|
package/lib/hooks/useMMKV.js
CHANGED
|
@@ -5,9 +5,13 @@ function isConfigurationEqual(left, right) {
|
|
|
5
5
|
if (left == null || right == null)
|
|
6
6
|
return left == null && right == null;
|
|
7
7
|
return (left.encryptionKey === right.encryptionKey &&
|
|
8
|
+
left.encryptionType === right.encryptionType &&
|
|
8
9
|
left.id === right.id &&
|
|
9
10
|
left.path === right.path &&
|
|
10
|
-
left.mode === right.mode
|
|
11
|
+
left.mode === right.mode &&
|
|
12
|
+
left.readOnly === right.readOnly &&
|
|
13
|
+
left.compareBeforeSet === right.compareBeforeSet &&
|
|
14
|
+
left.recoveryStrategy === right.recoveryStrategy);
|
|
11
15
|
}
|
|
12
16
|
export function useMMKV(configuration) {
|
|
13
17
|
const instance = useRef(undefined);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { MMKV } from './specs/MMKV.nitro';
|
|
2
|
-
export type { Configuration, Mode } from './specs/MMKVFactory.nitro';
|
|
2
|
+
export type { Configuration, Mode, RecoveryStrategy, } from './specs/MMKVFactory.nitro';
|
|
3
3
|
export { createMMKV } from './createMMKV/createMMKV';
|
|
4
4
|
export { existsMMKV } from './existsMMKV/existsMMKV';
|
|
5
5
|
export { deleteMMKV } from './deleteMMKV/deleteMMKV';
|
|
@@ -134,6 +134,11 @@ export interface MMKV extends HybridObject<{
|
|
|
134
134
|
* In most applications, this is not needed at all.
|
|
135
135
|
*/
|
|
136
136
|
trim(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Checks whether the underlying storage file changed outside this process,
|
|
139
|
+
* and reloads this instance's data if needed.
|
|
140
|
+
*/
|
|
141
|
+
checkContentChanged(): void;
|
|
137
142
|
/**
|
|
138
143
|
* Adds a value changed listener. The Listener will be called whenever any value
|
|
139
144
|
* in this storage instance changes (set or delete).
|
|
@@ -12,6 +12,12 @@ export type Mode = 'single-process' | 'multi-process';
|
|
|
12
12
|
* - `AES-256`: Uses AES-256 encryption for enhanced security.
|
|
13
13
|
*/
|
|
14
14
|
export type EncryptionType = 'AES-128' | 'AES-256';
|
|
15
|
+
/**
|
|
16
|
+
* Configures how MMKV should handle recoverable storage errors.
|
|
17
|
+
* - `discard-on-error`: Discards data when MMKV detects a CRC or file-length error.
|
|
18
|
+
* - `recover-on-error`: Attempts to recover data when MMKV detects a CRC or file-length error.
|
|
19
|
+
*/
|
|
20
|
+
export type RecoveryStrategy = 'discard-on-error' | 'recover-on-error';
|
|
15
21
|
/**
|
|
16
22
|
* Used for configuration of a single MMKV instance.
|
|
17
23
|
*/
|
|
@@ -91,6 +97,12 @@ export interface Configuration {
|
|
|
91
97
|
* @default false
|
|
92
98
|
*/
|
|
93
99
|
compareBeforeSet?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Configure how MMKV should handle recoverable storage errors.
|
|
102
|
+
*
|
|
103
|
+
* @default undefined
|
|
104
|
+
*/
|
|
105
|
+
recoveryStrategy?: RecoveryStrategy;
|
|
94
106
|
}
|
|
95
107
|
export interface MMKVFactory extends HybridObject<{
|
|
96
108
|
ios: 'c++';
|
package/nitro.json
CHANGED
|
@@ -14,11 +14,20 @@
|
|
|
14
14
|
},
|
|
15
15
|
"autolinking": {
|
|
16
16
|
"MMKVFactory": {
|
|
17
|
-
"
|
|
17
|
+
"all": {
|
|
18
|
+
"language": "c++",
|
|
19
|
+
"implementationClassName": "HybridMMKVFactory"
|
|
20
|
+
}
|
|
18
21
|
},
|
|
19
22
|
"MMKVPlatformContext": {
|
|
20
|
-
"
|
|
21
|
-
|
|
23
|
+
"ios": {
|
|
24
|
+
"language": "swift",
|
|
25
|
+
"implementationClassName": "HybridMMKVPlatformContext"
|
|
26
|
+
},
|
|
27
|
+
"android": {
|
|
28
|
+
"language": "kotlin",
|
|
29
|
+
"implementationClassName": "HybridMMKVPlatformContext"
|
|
30
|
+
}
|
|
22
31
|
}
|
|
23
32
|
},
|
|
24
33
|
"ignorePaths": [
|
|
@@ -28,9 +28,9 @@ int initialize(JavaVM* vm) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
struct JHybridMMKVPlatformContextSpecImpl: public jni::JavaClass<JHybridMMKVPlatformContextSpecImpl, JHybridMMKVPlatformContextSpec::JavaPart> {
|
|
31
|
-
static auto
|
|
31
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContext;";
|
|
32
32
|
static std::shared_ptr<JHybridMMKVPlatformContextSpec> create() {
|
|
33
|
-
static auto constructorFn = javaClassStatic()->getConstructor<JHybridMMKVPlatformContextSpecImpl::javaobject()>();
|
|
33
|
+
static const auto constructorFn = javaClassStatic()->getConstructor<JHybridMMKVPlatformContextSpecImpl::javaobject()>();
|
|
34
34
|
jni::local_ref<JHybridMMKVPlatformContextSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
|
|
35
35
|
return javaPart->getJHybridMMKVPlatformContextSpec();
|
|
36
36
|
}
|
|
@@ -21,11 +21,11 @@ namespace margelo::nitro::mmkv {
|
|
|
21
21
|
class JHybridMMKVPlatformContextSpec: public virtual HybridMMKVPlatformContextSpec, public virtual JHybridObject {
|
|
22
22
|
public:
|
|
23
23
|
struct JavaPart: public jni::JavaClass<JavaPart, JHybridObject::JavaPart> {
|
|
24
|
-
static auto
|
|
24
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec;";
|
|
25
25
|
std::shared_ptr<JHybridMMKVPlatformContextSpec> getJHybridMMKVPlatformContextSpec();
|
|
26
26
|
};
|
|
27
27
|
struct CxxPart: public jni::HybridClass<CxxPart, JHybridObject::CxxPart> {
|
|
28
|
-
static auto
|
|
28
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec$CxxPart;";
|
|
29
29
|
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
30
30
|
static void registerNatives();
|
|
31
31
|
using HybridBase::HybridBase;
|
|
@@ -56,5 +56,7 @@ def add_nitrogen_files(spec)
|
|
|
56
56
|
"SWIFT_OBJC_INTEROP_MODE" => "objcxx",
|
|
57
57
|
# Enables stricter modular headers
|
|
58
58
|
"DEFINES_MODULE" => "YES",
|
|
59
|
+
# Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here)
|
|
60
|
+
"SWIFT_INSTALL_OBJC_HEADER" => "NO",
|
|
59
61
|
})
|
|
60
62
|
end
|
|
@@ -32,11 +32,14 @@
|
|
|
32
32
|
namespace margelo::nitro::mmkv { enum class EncryptionType; }
|
|
33
33
|
// Forward declaration of `Mode` to properly resolve imports.
|
|
34
34
|
namespace margelo::nitro::mmkv { enum class Mode; }
|
|
35
|
+
// Forward declaration of `RecoveryStrategy` to properly resolve imports.
|
|
36
|
+
namespace margelo::nitro::mmkv { enum class RecoveryStrategy; }
|
|
35
37
|
|
|
36
38
|
#include <string>
|
|
37
39
|
#include <optional>
|
|
38
40
|
#include "EncryptionType.hpp"
|
|
39
41
|
#include "Mode.hpp"
|
|
42
|
+
#include "RecoveryStrategy.hpp"
|
|
40
43
|
|
|
41
44
|
namespace margelo::nitro::mmkv {
|
|
42
45
|
|
|
@@ -52,10 +55,11 @@ namespace margelo::nitro::mmkv {
|
|
|
52
55
|
std::optional<Mode> mode SWIFT_PRIVATE;
|
|
53
56
|
std::optional<bool> readOnly SWIFT_PRIVATE;
|
|
54
57
|
std::optional<bool> compareBeforeSet SWIFT_PRIVATE;
|
|
58
|
+
std::optional<RecoveryStrategy> recoveryStrategy SWIFT_PRIVATE;
|
|
55
59
|
|
|
56
60
|
public:
|
|
57
61
|
Configuration() = default;
|
|
58
|
-
explicit Configuration(std::string id, std::optional<std::string> path, std::optional<std::string> encryptionKey, std::optional<EncryptionType> encryptionType, std::optional<Mode> mode, std::optional<bool> readOnly, std::optional<bool> compareBeforeSet): id(id), path(path), encryptionKey(encryptionKey), encryptionType(encryptionType), mode(mode), readOnly(readOnly), compareBeforeSet(compareBeforeSet) {}
|
|
62
|
+
explicit Configuration(std::string id, std::optional<std::string> path, std::optional<std::string> encryptionKey, std::optional<EncryptionType> encryptionType, std::optional<Mode> mode, std::optional<bool> readOnly, std::optional<bool> compareBeforeSet, std::optional<RecoveryStrategy> recoveryStrategy): id(id), path(path), encryptionKey(encryptionKey), encryptionType(encryptionType), mode(mode), readOnly(readOnly), compareBeforeSet(compareBeforeSet), recoveryStrategy(recoveryStrategy) {}
|
|
59
63
|
|
|
60
64
|
public:
|
|
61
65
|
friend bool operator==(const Configuration& lhs, const Configuration& rhs) = default;
|
|
@@ -77,7 +81,8 @@ namespace margelo::nitro {
|
|
|
77
81
|
JSIConverter<std::optional<margelo::nitro::mmkv::EncryptionType>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "encryptionType"))),
|
|
78
82
|
JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode"))),
|
|
79
83
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly"))),
|
|
80
|
-
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet")))
|
|
84
|
+
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet"))),
|
|
85
|
+
JSIConverter<std::optional<margelo::nitro::mmkv::RecoveryStrategy>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "recoveryStrategy")))
|
|
81
86
|
);
|
|
82
87
|
}
|
|
83
88
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::mmkv::Configuration& arg) {
|
|
@@ -89,6 +94,7 @@ namespace margelo::nitro {
|
|
|
89
94
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "mode"), JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::toJSI(runtime, arg.mode));
|
|
90
95
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "readOnly"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.readOnly));
|
|
91
96
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.compareBeforeSet));
|
|
97
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "recoveryStrategy"), JSIConverter<std::optional<margelo::nitro::mmkv::RecoveryStrategy>>::toJSI(runtime, arg.recoveryStrategy));
|
|
92
98
|
return obj;
|
|
93
99
|
}
|
|
94
100
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -106,6 +112,7 @@ namespace margelo::nitro {
|
|
|
106
112
|
if (!JSIConverter<std::optional<margelo::nitro::mmkv::Mode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mode")))) return false;
|
|
107
113
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "readOnly")))) return false;
|
|
108
114
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compareBeforeSet")))) return false;
|
|
115
|
+
if (!JSIConverter<std::optional<margelo::nitro::mmkv::RecoveryStrategy>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "recoveryStrategy")))) return false;
|
|
109
116
|
return true;
|
|
110
117
|
}
|
|
111
118
|
};
|
|
@@ -33,6 +33,7 @@ namespace margelo::nitro::mmkv {
|
|
|
33
33
|
prototype.registerHybridMethod("encrypt", &HybridMMKVSpec::encrypt);
|
|
34
34
|
prototype.registerHybridMethod("decrypt", &HybridMMKVSpec::decrypt);
|
|
35
35
|
prototype.registerHybridMethod("trim", &HybridMMKVSpec::trim);
|
|
36
|
+
prototype.registerHybridMethod("checkContentChanged", &HybridMMKVSpec::checkContentChanged);
|
|
36
37
|
prototype.registerHybridMethod("addOnValueChangedListener", &HybridMMKVSpec::addOnValueChangedListener);
|
|
37
38
|
prototype.registerHybridMethod("importAllFrom", &HybridMMKVSpec::importAllFrom);
|
|
38
39
|
});
|
|
@@ -80,6 +80,7 @@ namespace margelo::nitro::mmkv {
|
|
|
80
80
|
virtual void encrypt(const std::string& key, std::optional<EncryptionType> encryptionType) = 0;
|
|
81
81
|
virtual void decrypt() = 0;
|
|
82
82
|
virtual void trim() = 0;
|
|
83
|
+
virtual void checkContentChanged() = 0;
|
|
83
84
|
virtual Listener addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) = 0;
|
|
84
85
|
virtual double importAllFrom(const std::shared_ptr<HybridMMKVSpec>& other) = 0;
|
|
85
86
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// RecoveryStrategy.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 (RecoveryStrategy).
|
|
30
|
+
*/
|
|
31
|
+
enum class RecoveryStrategy {
|
|
32
|
+
DISCARD_ON_ERROR SWIFT_NAME(discardOnError) = 0,
|
|
33
|
+
RECOVER_ON_ERROR SWIFT_NAME(recoverOnError) = 1,
|
|
34
|
+
} CLOSED_ENUM;
|
|
35
|
+
|
|
36
|
+
} // namespace margelo::nitro::mmkv
|
|
37
|
+
|
|
38
|
+
namespace margelo::nitro {
|
|
39
|
+
|
|
40
|
+
// C++ RecoveryStrategy <> JS RecoveryStrategy (union)
|
|
41
|
+
template <>
|
|
42
|
+
struct JSIConverter<margelo::nitro::mmkv::RecoveryStrategy> final {
|
|
43
|
+
static inline margelo::nitro::mmkv::RecoveryStrategy 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("discard-on-error"): return margelo::nitro::mmkv::RecoveryStrategy::DISCARD_ON_ERROR;
|
|
47
|
+
case hashString("recover-on-error"): return margelo::nitro::mmkv::RecoveryStrategy::RECOVER_ON_ERROR;
|
|
48
|
+
default: [[unlikely]]
|
|
49
|
+
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum RecoveryStrategy - invalid value!");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::mmkv::RecoveryStrategy arg) {
|
|
53
|
+
switch (arg) {
|
|
54
|
+
case margelo::nitro::mmkv::RecoveryStrategy::DISCARD_ON_ERROR: return JSIConverter<std::string>::toJSI(runtime, "discard-on-error");
|
|
55
|
+
case margelo::nitro::mmkv::RecoveryStrategy::RECOVER_ON_ERROR: return JSIConverter<std::string>::toJSI(runtime, "recover-on-error");
|
|
56
|
+
default: [[unlikely]]
|
|
57
|
+
throw std::invalid_argument("Cannot convert RecoveryStrategy 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("discard-on-error"):
|
|
68
|
+
case hashString("recover-on-error"):
|
|
69
|
+
return true;
|
|
70
|
+
default:
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
} // namespace margelo::nitro
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mmkv",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "⚡️ The fastest key/value storage for React Native.",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"clean": "rm -rf android/build node_modules/**/android/build lib",
|
|
36
|
-
"lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
|
37
|
-
"lint-ci": "eslint \"**/*.{js,ts,tsx}\" -f @jamesacarr/github-actions",
|
|
36
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\" --fix && prettier --write \"**/*.{js,ts,tsx}\"",
|
|
37
|
+
"lint-ci": "eslint \"**/*.{js,ts,tsx}\" --max-warnings=0 -f @jamesacarr/github-actions && prettier --check \"**/*.{js,ts,tsx}\"",
|
|
38
38
|
"typescript": "tsc",
|
|
39
39
|
"specs": "tsc && nitrogen --logLevel=\"debug\"",
|
|
40
40
|
"build": "tsc --noEmit false",
|
|
@@ -59,20 +59,19 @@
|
|
|
59
59
|
"registry": "https://registry.npmjs.org/"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@
|
|
63
|
-
"@react-native/
|
|
62
|
+
"@react-native/eslint-config": "0.85.3",
|
|
63
|
+
"@react-native/jest-preset": "0.85.3",
|
|
64
64
|
"@testing-library/react-native": "^13.3.1",
|
|
65
|
-
"@types/jest": "^29.5.
|
|
66
|
-
"@types/react": "^19.
|
|
67
|
-
"eslint": "^8.57.
|
|
68
|
-
"eslint-config-prettier": "^
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"react": "
|
|
73
|
-
"react-native": "0.
|
|
74
|
-
"
|
|
75
|
-
"typescript": "^5.8.3"
|
|
65
|
+
"@types/jest": "^29.5.14",
|
|
66
|
+
"@types/react": "^19.2.15",
|
|
67
|
+
"eslint": "^8.57.1",
|
|
68
|
+
"eslint-config-prettier": "^10.1.8",
|
|
69
|
+
"nitrogen": "0.35.9",
|
|
70
|
+
"prettier": "^3.8.3",
|
|
71
|
+
"react": "19.2.6",
|
|
72
|
+
"react-native": "0.85.3",
|
|
73
|
+
"react-native-nitro-modules": "0.35.9",
|
|
74
|
+
"typescript": "^6.0.3"
|
|
76
75
|
},
|
|
77
76
|
"peerDependencies": {
|
|
78
77
|
"react": "*",
|
|
@@ -84,22 +83,7 @@
|
|
|
84
83
|
"extends": [
|
|
85
84
|
"@react-native",
|
|
86
85
|
"prettier"
|
|
87
|
-
]
|
|
88
|
-
"plugins": [
|
|
89
|
-
"prettier"
|
|
90
|
-
],
|
|
91
|
-
"rules": {
|
|
92
|
-
"prettier/prettier": [
|
|
93
|
-
"warn",
|
|
94
|
-
{
|
|
95
|
-
"quoteProps": "consistent",
|
|
96
|
-
"singleQuote": true,
|
|
97
|
-
"tabWidth": 2,
|
|
98
|
-
"trailingComma": "es5",
|
|
99
|
-
"useTabs": false
|
|
100
|
-
}
|
|
101
|
-
]
|
|
102
|
-
}
|
|
86
|
+
]
|
|
103
87
|
},
|
|
104
88
|
"eslintIgnore": [
|
|
105
89
|
"node_modules/",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AppState } from 'react-native'
|
|
2
|
+
import type { AppStateStatus, NativeEventSubscription } from 'react-native'
|
|
3
|
+
import { createMockMMKV } from '../createMMKV/createMockMMKV'
|
|
4
|
+
import { addContentChangedListener } from '../addContentChangedListener/addContentChangedListener'
|
|
5
|
+
|
|
6
|
+
function mockAppStateChangeListener(): {
|
|
7
|
+
emit: (state: AppStateStatus) => void
|
|
8
|
+
} {
|
|
9
|
+
const listeners = new Set<(state: AppStateStatus) => void>()
|
|
10
|
+
|
|
11
|
+
jest
|
|
12
|
+
.spyOn(AppState, 'addEventListener')
|
|
13
|
+
.mockImplementation((type, listener): NativeEventSubscription => {
|
|
14
|
+
if (type === 'change') {
|
|
15
|
+
listeners.add(listener as (state: AppStateStatus) => void)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
remove: () => {
|
|
20
|
+
listeners.delete(listener as (state: AppStateStatus) => void)
|
|
21
|
+
},
|
|
22
|
+
} as NativeEventSubscription
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
emit: (state) => {
|
|
27
|
+
listeners.forEach((listener) => listener(state))
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
jest.clearAllMocks()
|
|
34
|
+
jest.restoreAllMocks()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('content changed listener checks content when app becomes active', () => {
|
|
38
|
+
const appState = mockAppStateChangeListener()
|
|
39
|
+
const storage = createMockMMKV({ id: 'content-changed-listener-test' })
|
|
40
|
+
const checkContentChanged = jest.spyOn(storage, 'checkContentChanged')
|
|
41
|
+
|
|
42
|
+
addContentChangedListener(storage)
|
|
43
|
+
|
|
44
|
+
appState.emit('background')
|
|
45
|
+
expect(checkContentChanged).not.toHaveBeenCalled()
|
|
46
|
+
|
|
47
|
+
appState.emit('active')
|
|
48
|
+
expect(checkContentChanged).toHaveBeenCalledTimes(1)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('content changed listener registers once per instance', () => {
|
|
52
|
+
mockAppStateChangeListener()
|
|
53
|
+
const storage = createMockMMKV({
|
|
54
|
+
id: 'content-changed-listener-once-test',
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
addContentChangedListener(storage)
|
|
58
|
+
addContentChangedListener(storage)
|
|
59
|
+
|
|
60
|
+
expect(AppState.addEventListener).toHaveBeenCalledTimes(1)
|
|
61
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AppState } from 'react-native'
|
|
2
|
+
import type { AppStateStatus, NativeEventSubscription } from 'react-native'
|
|
3
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
4
|
+
|
|
5
|
+
const registeredInstances = new WeakSet<MMKV>()
|
|
6
|
+
|
|
7
|
+
export function addContentChangedListener(mmkv: MMKV): void {
|
|
8
|
+
if (registeredInstances.has(mmkv)) {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
registeredInstances.add(mmkv)
|
|
12
|
+
|
|
13
|
+
if (global.WeakRef != null && global.FinalizationRegistry != null) {
|
|
14
|
+
const weakMmkv = new WeakRef(mmkv)
|
|
15
|
+
const listener = AppState.addEventListener(
|
|
16
|
+
'change',
|
|
17
|
+
(state: AppStateStatus) => {
|
|
18
|
+
if (state === 'active') {
|
|
19
|
+
weakMmkv.deref()?.checkContentChanged()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
const finalization = new FinalizationRegistry(
|
|
24
|
+
(l: NativeEventSubscription) => {
|
|
25
|
+
l.remove()
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
finalization.register(mmkv, listener)
|
|
29
|
+
} else {
|
|
30
|
+
AppState.addEventListener('change', (state: AppStateStatus) => {
|
|
31
|
+
if (state === 'active') {
|
|
32
|
+
mmkv.checkContentChanged()
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { MMKV } from '../specs/MMKV.nitro'
|
|
|
2
2
|
import type { Configuration } from '../specs/MMKVFactory.nitro'
|
|
3
3
|
import { Platform } from 'react-native'
|
|
4
4
|
import { addMemoryWarningListener } from '../addMemoryWarningListener/addMemoryWarningListener'
|
|
5
|
+
import { addContentChangedListener } from '../addContentChangedListener/addContentChangedListener'
|
|
5
6
|
import { isTest } from '../isTest'
|
|
6
7
|
import { createMockMMKV } from './createMockMMKV'
|
|
7
8
|
import { getMMKVFactory, getPlatformContext } from '../getMMKVFactory'
|
|
@@ -33,5 +34,7 @@ export function createMMKV(configuration?: Configuration): MMKV {
|
|
|
33
34
|
const mmkv = factory.createMMKV(config)
|
|
34
35
|
// Add a hook that trims the storage when we get a memory warning
|
|
35
36
|
addMemoryWarningListener(mmkv)
|
|
37
|
+
// Check for updates from app extensions/App Clips/background services when the app becomes active
|
|
38
|
+
addContentChangedListener(mmkv)
|
|
36
39
|
return mmkv
|
|
37
40
|
}
|
|
@@ -42,7 +42,8 @@ export function createMMKV(
|
|
|
42
42
|
return {
|
|
43
43
|
id: config.id,
|
|
44
44
|
get length(): number {
|
|
45
|
-
|
|
45
|
+
const keys = this.getAllKeys()
|
|
46
|
+
return keys.length
|
|
46
47
|
},
|
|
47
48
|
get size(): number {
|
|
48
49
|
return this.byteSize
|
|
@@ -125,6 +126,9 @@ export function createMMKV(
|
|
|
125
126
|
trim: () => {
|
|
126
127
|
// no-op
|
|
127
128
|
},
|
|
129
|
+
checkContentChanged: () => {
|
|
130
|
+
// no-op
|
|
131
|
+
},
|
|
128
132
|
dispose: () => {},
|
|
129
133
|
equals: () => false,
|
|
130
134
|
name: 'MMKV',
|
package/src/hooks/useMMKV.ts
CHANGED
|
@@ -12,9 +12,13 @@ function isConfigurationEqual(
|
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
left.encryptionKey === right.encryptionKey &&
|
|
15
|
+
left.encryptionType === right.encryptionType &&
|
|
15
16
|
left.id === right.id &&
|
|
16
17
|
left.path === right.path &&
|
|
17
|
-
left.mode === right.mode
|
|
18
|
+
left.mode === right.mode &&
|
|
19
|
+
left.readOnly === right.readOnly &&
|
|
20
|
+
left.compareBeforeSet === right.compareBeforeSet &&
|
|
21
|
+
left.recoveryStrategy === right.recoveryStrategy
|
|
18
22
|
)
|
|
19
23
|
}
|
|
20
24
|
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// All types
|
|
2
2
|
export type { MMKV } from './specs/MMKV.nitro'
|
|
3
|
-
export type {
|
|
3
|
+
export type {
|
|
4
|
+
Configuration,
|
|
5
|
+
Mode,
|
|
6
|
+
RecoveryStrategy,
|
|
7
|
+
} from './specs/MMKVFactory.nitro'
|
|
4
8
|
|
|
5
9
|
// The create function
|
|
6
10
|
export { createMMKV } from './createMMKV/createMMKV'
|
package/src/specs/MMKV.nitro.ts
CHANGED
|
@@ -133,6 +133,11 @@ export interface MMKV extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
|
|
|
133
133
|
* In most applications, this is not needed at all.
|
|
134
134
|
*/
|
|
135
135
|
trim(): void
|
|
136
|
+
/**
|
|
137
|
+
* Checks whether the underlying storage file changed outside this process,
|
|
138
|
+
* and reloads this instance's data if needed.
|
|
139
|
+
*/
|
|
140
|
+
checkContentChanged(): void
|
|
136
141
|
/**
|
|
137
142
|
* Adds a value changed listener. The Listener will be called whenever any value
|
|
138
143
|
* in this storage instance changes (set or delete).
|
|
@@ -15,6 +15,13 @@ export type Mode = 'single-process' | 'multi-process'
|
|
|
15
15
|
*/
|
|
16
16
|
export type EncryptionType = 'AES-128' | 'AES-256'
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Configures how MMKV should handle recoverable storage errors.
|
|
20
|
+
* - `discard-on-error`: Discards data when MMKV detects a CRC or file-length error.
|
|
21
|
+
* - `recover-on-error`: Attempts to recover data when MMKV detects a CRC or file-length error.
|
|
22
|
+
*/
|
|
23
|
+
export type RecoveryStrategy = 'discard-on-error' | 'recover-on-error'
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* Used for configuration of a single MMKV instance.
|
|
20
27
|
*/
|
|
@@ -94,10 +101,18 @@ export interface Configuration {
|
|
|
94
101
|
* @default false
|
|
95
102
|
*/
|
|
96
103
|
compareBeforeSet?: boolean
|
|
104
|
+
/**
|
|
105
|
+
* Configure how MMKV should handle recoverable storage errors.
|
|
106
|
+
*
|
|
107
|
+
* @default undefined
|
|
108
|
+
*/
|
|
109
|
+
recoveryStrategy?: RecoveryStrategy
|
|
97
110
|
}
|
|
98
111
|
|
|
99
|
-
export interface MMKVFactory
|
|
100
|
-
|
|
112
|
+
export interface MMKVFactory extends HybridObject<{
|
|
113
|
+
ios: 'c++'
|
|
114
|
+
android: 'c++'
|
|
115
|
+
}> {
|
|
101
116
|
/**
|
|
102
117
|
* Initialize the MMKV library with the given root path.
|
|
103
118
|
* This has to be called once, before using {@linkcode createMMKV}.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { HybridObject } from 'react-native-nitro-modules'
|
|
2
2
|
|
|
3
|
-
export interface MMKVPlatformContext
|
|
4
|
-
|
|
3
|
+
export interface MMKVPlatformContext extends HybridObject<{
|
|
4
|
+
ios: 'swift'
|
|
5
|
+
android: 'kotlin'
|
|
6
|
+
}> {
|
|
5
7
|
/**
|
|
6
8
|
* Get the MMKV base directory
|
|
7
9
|
*/
|