react-native-mmkv 4.0.0-beta.1 → 4.0.0-beta.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/{react-native-mmkv.podspec → NitroMmkv.podspec} +17 -13
- package/README.md +1 -3
- package/android/CMakeLists.txt +31 -30
- package/android/build.gradle +64 -19
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -5
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/mmkv/HybridMMKVPlatformContext.kt +19 -0
- package/android/src/main/java/com/margelo/nitro/mmkv/NitroMmkvPackage.java +33 -0
- package/ios/HybridMMKVPlatformContext.swift +32 -0
- package/lib/__tests__/hooks.test.d.ts +1 -0
- package/lib/__tests__/hooks.test.js +66 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.d.ts +2 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.js +25 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.d.ts +2 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.js +3 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.web.d.ts +2 -0
- package/lib/addMemoryWarningListener/addMemoryWarningListener.web.js +3 -0
- package/lib/createMMKV/createMMKV.d.ts +3 -0
- package/lib/createMMKV/createMMKV.js +40 -0
- package/lib/createMMKV/createMMKV.mock.d.ts +5 -0
- package/lib/createMMKV/createMMKV.mock.js +53 -0
- package/lib/createMMKV/createMMKV.web.d.ts +3 -0
- package/lib/createMMKV/createMMKV.web.js +117 -0
- package/lib/createMMKV/getDefaultMMKVInstance.d.ts +2 -0
- package/lib/createMMKV/getDefaultMMKVInstance.js +8 -0
- package/lib/hooks/createMMKVHook.d.ts +2 -0
- package/lib/hooks/createMMKVHook.js +49 -0
- package/lib/hooks/useMMKV.d.ts +11 -0
- package/lib/hooks/useMMKV.js +23 -0
- package/lib/hooks/useMMKVBoolean.d.ts +11 -0
- package/lib/hooks/useMMKVBoolean.js +12 -0
- package/lib/hooks/useMMKVBuffer.d.ts +11 -0
- package/lib/hooks/useMMKVBuffer.js +12 -0
- package/lib/hooks/useMMKVKeys.d.ts +12 -0
- package/lib/hooks/useMMKVKeys.js +33 -0
- package/lib/hooks/useMMKVListener.d.ts +15 -0
- package/lib/hooks/useMMKVListener.js +26 -0
- package/lib/hooks/useMMKVNumber.d.ts +11 -0
- package/lib/hooks/useMMKVNumber.js +12 -0
- package/lib/hooks/useMMKVObject.d.ts +17 -0
- package/lib/hooks/useMMKVObject.js +38 -0
- package/lib/hooks/useMMKVString.d.ts +11 -0
- package/lib/hooks/useMMKVString.js +12 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +11 -0
- package/lib/isTest.d.ts +1 -0
- package/lib/isTest.js +7 -0
- package/lib/specs/MMKV.nitro.d.ts +93 -0
- package/lib/specs/MMKV.nitro.js +1 -0
- package/lib/{typescript/src/NativeMmkv.d.ts → specs/MMKVFactory.nitro.d.ts} +22 -29
- package/lib/specs/MMKVFactory.nitro.js +1 -0
- package/lib/specs/MMKVPlatformContext.nitro.d.ts +15 -0
- package/lib/specs/MMKVPlatformContext.nitro.js +1 -0
- package/lib/{typescript/src → web}/createTextEncoder.d.ts +0 -1
- package/lib/web/createTextEncoder.js +17 -0
- package/nitro.json +27 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroMmkv+autolinking.cmake +80 -0
- package/nitrogen/generated/android/NitroMmkv+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +55 -0
- package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +25 -0
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +51 -0
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/NitroMmkvOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NitroMmkv+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.cpp +32 -0
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +52 -0
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Umbrella.hpp +44 -0
- package/nitrogen/generated/ios/NitroMmkvAutolinking.mm +43 -0
- package/nitrogen/generated/ios/NitroMmkvAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +81 -0
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +50 -0
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +135 -0
- package/nitrogen/generated/shared/c++/Configuration.hpp +86 -0
- package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.hpp +69 -0
- package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.hpp +63 -0
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +34 -0
- package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +83 -0
- package/nitrogen/generated/shared/c++/Listener.hpp +67 -0
- package/nitrogen/generated/shared/c++/Mode.hpp +76 -0
- package/package.json +71 -122
- package/react-native.config.js +2 -15
- package/src/__tests__/hooks.test.tsx +34 -34
- package/src/addMemoryWarningListener/addMemoryWarningListener.mock.ts +5 -0
- package/src/{MemoryWarningListener.ts → addMemoryWarningListener/addMemoryWarningListener.ts} +12 -12
- package/src/addMemoryWarningListener/addMemoryWarningListener.web.ts +5 -0
- package/src/createMMKV/createMMKV.mock.ts +56 -0
- package/src/createMMKV/createMMKV.ts +51 -0
- package/src/{createMMKV.web.ts → createMMKV/createMMKV.web.ts} +56 -46
- package/src/createMMKV/getDefaultMMKVInstance.ts +10 -0
- package/src/hooks/createMMKVHook.ts +66 -0
- package/src/hooks/useMMKV.ts +45 -0
- package/src/hooks/useMMKVBoolean.ts +15 -0
- package/src/hooks/useMMKVBuffer.ts +15 -0
- package/src/hooks/useMMKVKeys.ts +36 -0
- package/src/hooks/useMMKVListener.ts +33 -0
- package/src/hooks/useMMKVNumber.ts +15 -0
- package/src/hooks/useMMKVObject.ts +53 -0
- package/src/hooks/useMMKVString.ts +15 -0
- package/src/index.ts +15 -3
- package/src/{PlatformChecker.ts → isTest.ts} +2 -2
- package/src/specs/MMKV.nitro.ts +92 -0
- package/src/specs/MMKVFactory.nitro.ts +87 -0
- package/src/specs/MMKVPlatformContext.nitro.ts +14 -0
- package/src/{createTextEncoder.ts → web/createTextEncoder.ts} +7 -7
- package/android/src/main/cpp/AndroidLogger.cpp +0 -16
- package/android/src/main/java/com/mrousavy/mmkv/MmkvPackage.java +0 -44
- package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java +0 -26
- package/cpp/ManagedMMBuffer.h +0 -32
- package/cpp/MmkvHostObject.cpp +0 -360
- package/cpp/MmkvHostObject.h +0 -31
- package/cpp/MmkvLogger.h +0 -35
- package/cpp/MmkvTypes.h +0 -50
- package/cpp/NativeMmkvModule.cpp +0 -43
- package/cpp/NativeMmkvModule.h +0 -31
- package/ios/AppleLogger.mm +0 -16
- package/ios/MmkvOnLoad.mm +0 -25
- package/ios/MmkvPlatformContext.h +0 -19
- package/ios/MmkvPlatformContextModule.mm +0 -55
- package/lib/commonjs/MMKV.js +0 -124
- package/lib/commonjs/MMKV.js.map +0 -1
- package/lib/commonjs/MemoryWarningListener.js +0 -31
- package/lib/commonjs/MemoryWarningListener.js.map +0 -1
- package/lib/commonjs/MemoryWarningListener.web.js +0 -11
- package/lib/commonjs/MemoryWarningListener.web.js.map +0 -1
- package/lib/commonjs/ModuleNotFoundError.js +0 -75
- package/lib/commonjs/ModuleNotFoundError.js.map +0 -1
- package/lib/commonjs/NativeMmkv.js +0 -47
- package/lib/commonjs/NativeMmkv.js.map +0 -1
- package/lib/commonjs/NativeMmkvPlatformContext.js +0 -22
- package/lib/commonjs/NativeMmkvPlatformContext.js.map +0 -1
- package/lib/commonjs/PlatformChecker.js +0 -14
- package/lib/commonjs/PlatformChecker.js.map +0 -1
- package/lib/commonjs/Types.js +0 -26
- package/lib/commonjs/Types.js.map +0 -1
- package/lib/commonjs/createMMKV.js +0 -43
- package/lib/commonjs/createMMKV.js.map +0 -1
- package/lib/commonjs/createMMKV.mock.js +0 -43
- package/lib/commonjs/createMMKV.mock.js.map +0 -1
- package/lib/commonjs/createMMKV.web.js +0 -110
- package/lib/commonjs/createMMKV.web.js.map +0 -1
- package/lib/commonjs/createTextEncoder.js +0 -23
- package/lib/commonjs/createTextEncoder.js.map +0 -1
- package/lib/commonjs/hooks.js +0 -198
- package/lib/commonjs/hooks.js.map +0 -1
- package/lib/commonjs/index.js +0 -40
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/package.json +0 -1
- package/lib/module/MMKV.js +0 -119
- package/lib/module/MMKV.js.map +0 -1
- package/lib/module/MemoryWarningListener.js +0 -27
- package/lib/module/MemoryWarningListener.js.map +0 -1
- package/lib/module/MemoryWarningListener.web.js +0 -6
- package/lib/module/MemoryWarningListener.web.js.map +0 -1
- package/lib/module/ModuleNotFoundError.js +0 -70
- package/lib/module/ModuleNotFoundError.js.map +0 -1
- package/lib/module/NativeMmkv.js +0 -45
- package/lib/module/NativeMmkv.js.map +0 -1
- package/lib/module/NativeMmkvPlatformContext.js +0 -18
- package/lib/module/NativeMmkvPlatformContext.js.map +0 -1
- package/lib/module/PlatformChecker.js +0 -10
- package/lib/module/PlatformChecker.js.map +0 -1
- package/lib/module/Types.js +0 -25
- package/lib/module/Types.js.map +0 -1
- package/lib/module/createMMKV.js +0 -38
- package/lib/module/createMMKV.js.map +0 -1
- package/lib/module/createMMKV.mock.js +0 -38
- package/lib/module/createMMKV.mock.js.map +0 -1
- package/lib/module/createMMKV.web.js +0 -105
- package/lib/module/createMMKV.web.js.map +0 -1
- package/lib/module/createTextEncoder.js +0 -19
- package/lib/module/createTextEncoder.js.map +0 -1
- package/lib/module/hooks.js +0 -189
- package/lib/module/hooks.js.map +0 -1
- package/lib/module/index.js +0 -6
- package/lib/module/index.js.map +0 -1
- package/lib/module/package.json +0 -1
- package/lib/typescript/src/MMKV.d.ts +0 -34
- package/lib/typescript/src/MMKV.d.ts.map +0 -1
- package/lib/typescript/src/MemoryWarningListener.d.ts +0 -3
- package/lib/typescript/src/MemoryWarningListener.d.ts.map +0 -1
- package/lib/typescript/src/MemoryWarningListener.web.d.ts +0 -3
- package/lib/typescript/src/MemoryWarningListener.web.d.ts.map +0 -1
- package/lib/typescript/src/ModuleNotFoundError.d.ts +0 -7
- package/lib/typescript/src/ModuleNotFoundError.d.ts.map +0 -1
- package/lib/typescript/src/NativeMmkv.d.ts.map +0 -1
- package/lib/typescript/src/NativeMmkvPlatformContext.d.ts +0 -20
- package/lib/typescript/src/NativeMmkvPlatformContext.d.ts.map +0 -1
- package/lib/typescript/src/PlatformChecker.d.ts +0 -2
- package/lib/typescript/src/PlatformChecker.d.ts.map +0 -1
- package/lib/typescript/src/Types.d.ts +0 -172
- package/lib/typescript/src/Types.d.ts.map +0 -1
- package/lib/typescript/src/__tests__/hooks.test.d.ts +0 -2
- package/lib/typescript/src/__tests__/hooks.test.d.ts.map +0 -1
- package/lib/typescript/src/createMMKV.d.ts +0 -3
- package/lib/typescript/src/createMMKV.d.ts.map +0 -1
- package/lib/typescript/src/createMMKV.mock.d.ts +0 -3
- package/lib/typescript/src/createMMKV.mock.d.ts.map +0 -1
- package/lib/typescript/src/createMMKV.web.d.ts +0 -3
- package/lib/typescript/src/createMMKV.web.d.ts.map +0 -1
- package/lib/typescript/src/createTextEncoder.d.ts.map +0 -1
- package/lib/typescript/src/hooks.d.ts +0 -86
- package/lib/typescript/src/hooks.d.ts.map +0 -1
- package/lib/typescript/src/index.d.ts +0 -4
- package/lib/typescript/src/index.d.ts.map +0 -1
- package/src/MMKV.ts +0 -142
- package/src/MemoryWarningListener.web.ts +0 -5
- package/src/ModuleNotFoundError.ts +0 -95
- package/src/NativeMmkv.ts +0 -118
- package/src/NativeMmkvPlatformContext.ts +0 -38
- package/src/Types.ts +0 -178
- package/src/createMMKV.mock.ts +0 -38
- package/src/createMMKV.ts +0 -42
- package/src/hooks.ts +0 -247
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Button, Text } from 'react-native'
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Button, Text } from 'react-native'
|
|
3
3
|
import {
|
|
4
4
|
act,
|
|
5
5
|
fireEvent,
|
|
6
6
|
render,
|
|
7
7
|
renderHook,
|
|
8
8
|
screen,
|
|
9
|
-
} from '@testing-library/react-native'
|
|
10
|
-
import {
|
|
9
|
+
} from '@testing-library/react-native'
|
|
10
|
+
import { createMMKV, useMMKVNumber, useMMKVString } from '..'
|
|
11
11
|
|
|
12
|
-
const mmkv =
|
|
12
|
+
const mmkv = createMMKV()
|
|
13
13
|
|
|
14
14
|
beforeEach(() => {
|
|
15
|
-
mmkv.clearAll()
|
|
16
|
-
mmkv.trim()
|
|
17
|
-
})
|
|
15
|
+
mmkv.clearAll()
|
|
16
|
+
mmkv.trim()
|
|
17
|
+
})
|
|
18
18
|
|
|
19
19
|
test('hooks update when the value is changed directly through the instance', () => {
|
|
20
|
-
const { result } = renderHook(() => useMMKVString('string-key', mmkv))
|
|
20
|
+
const { result } = renderHook(() => useMMKVString('string-key', mmkv))
|
|
21
21
|
|
|
22
|
-
expect(result.current[0]).toBeUndefined()
|
|
22
|
+
expect(result.current[0]).toBeUndefined()
|
|
23
23
|
|
|
24
24
|
// First, make a "normal" change
|
|
25
25
|
act(() => {
|
|
26
|
-
result.current[1]('value 1')
|
|
27
|
-
})
|
|
26
|
+
result.current[1]('value 1')
|
|
27
|
+
})
|
|
28
28
|
|
|
29
|
-
expect(result.current[0]).toStrictEqual('value 1')
|
|
29
|
+
expect(result.current[0]).toStrictEqual('value 1')
|
|
30
30
|
|
|
31
31
|
// Now, make the change directly through the instance.
|
|
32
32
|
act(() => {
|
|
33
|
-
mmkv.set('string-key', 'value 2')
|
|
34
|
-
})
|
|
35
|
-
expect(result.current[0]).toStrictEqual('value 2')
|
|
36
|
-
})
|
|
33
|
+
mmkv.set('string-key', 'value 2')
|
|
34
|
+
})
|
|
35
|
+
expect(result.current[0]).toStrictEqual('value 2')
|
|
36
|
+
})
|
|
37
37
|
|
|
38
38
|
test('functional updates to hooks', () => {
|
|
39
39
|
const Component: React.FC = () => {
|
|
40
|
-
const [state, setState] = React.useState(0)
|
|
41
|
-
const [value, setValue] = useMMKVNumber('number-key', mmkv)
|
|
40
|
+
const [state, setState] = React.useState(0)
|
|
41
|
+
const [value, setValue] = useMMKVNumber('number-key', mmkv)
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
44
|
<>
|
|
@@ -47,43 +47,43 @@ test('functional updates to hooks', () => {
|
|
|
47
47
|
title="Double Increment Me"
|
|
48
48
|
onPress={() => {
|
|
49
49
|
// Increment the state value twice, using the function form of useState.
|
|
50
|
-
setState((current) => current + 1)
|
|
51
|
-
setState((current) => current + 1)
|
|
50
|
+
setState((current) => current + 1)
|
|
51
|
+
setState((current) => current + 1)
|
|
52
52
|
|
|
53
53
|
// Increment the MMKV value twice, using the same function form.
|
|
54
|
-
setValue((current) => (current ?? 0) + 1)
|
|
55
|
-
setValue((current) => (current ?? 0) + 1)
|
|
54
|
+
setValue((current) => (current ?? 0) + 1)
|
|
55
|
+
setValue((current) => (current ?? 0) + 1)
|
|
56
56
|
}}
|
|
57
57
|
/>
|
|
58
58
|
<Text testID="state-value">State: {state.toString()}</Text>
|
|
59
59
|
<Text testID="mmkv-value">MMKV: {(value ?? 0).toString()}</Text>
|
|
60
60
|
</>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
render(<Component />)
|
|
64
|
+
render(<Component />)
|
|
65
65
|
|
|
66
|
-
const button = screen.getByTestId('button')
|
|
66
|
+
const button = screen.getByTestId('button')
|
|
67
67
|
|
|
68
68
|
// Why these assertions:
|
|
69
69
|
// https://github.com/mrousavy/react-native-mmkv/issues/599
|
|
70
|
-
fireEvent.press(button)
|
|
70
|
+
fireEvent.press(button)
|
|
71
71
|
expect(screen.getByTestId('state-value').children).toStrictEqual([
|
|
72
72
|
'State: ',
|
|
73
73
|
'2',
|
|
74
|
-
])
|
|
74
|
+
])
|
|
75
75
|
expect(screen.getByTestId('mmkv-value').children).toStrictEqual([
|
|
76
76
|
'MMKV: ',
|
|
77
77
|
'2',
|
|
78
|
-
])
|
|
78
|
+
])
|
|
79
79
|
|
|
80
|
-
fireEvent.press(button)
|
|
80
|
+
fireEvent.press(button)
|
|
81
81
|
expect(screen.getByTestId('state-value').children).toStrictEqual([
|
|
82
82
|
'State: ',
|
|
83
83
|
'4',
|
|
84
|
-
])
|
|
84
|
+
])
|
|
85
85
|
expect(screen.getByTestId('mmkv-value').children).toStrictEqual([
|
|
86
86
|
'MMKV: ',
|
|
87
87
|
'4',
|
|
88
|
-
])
|
|
89
|
-
})
|
|
88
|
+
])
|
|
89
|
+
})
|
package/src/{MemoryWarningListener.ts → addMemoryWarningListener/addMemoryWarningListener.ts}
RENAMED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { AppState } from 'react-native'
|
|
2
|
-
import type { NativeEventSubscription } from 'react-native'
|
|
3
|
-
import {
|
|
1
|
+
import { AppState } from 'react-native'
|
|
2
|
+
import type { NativeEventSubscription } from 'react-native'
|
|
3
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
4
4
|
|
|
5
|
-
export function addMemoryWarningListener(mmkv:
|
|
5
|
+
export function addMemoryWarningListener(mmkv: MMKV): void {
|
|
6
6
|
if (global.WeakRef != null && global.FinalizationRegistry != null) {
|
|
7
7
|
// 1. Weakify MMKV so we can safely use it inside the memoryWarning event listener
|
|
8
|
-
const weakMmkv = new WeakRef(mmkv)
|
|
8
|
+
const weakMmkv = new WeakRef(mmkv)
|
|
9
9
|
const listener = AppState.addEventListener('memoryWarning', () => {
|
|
10
10
|
// 0. Everytime we receive a memoryWarning, we try to trim the MMKV instance (if it is still valid)
|
|
11
|
-
weakMmkv.deref()?.trim()
|
|
12
|
-
})
|
|
11
|
+
weakMmkv.deref()?.trim()
|
|
12
|
+
})
|
|
13
13
|
// 2. Add a listener to when the MMKV instance is deleted
|
|
14
14
|
const finalization = new FinalizationRegistry(
|
|
15
15
|
(l: NativeEventSubscription) => {
|
|
16
16
|
// 3. When MMKV is deleted, this listener will be called with the memoryWarning listener.
|
|
17
|
-
l.remove()
|
|
17
|
+
l.remove()
|
|
18
18
|
}
|
|
19
|
-
)
|
|
19
|
+
)
|
|
20
20
|
// 2.1. Bind the listener to the actual MMKV instance.
|
|
21
|
-
finalization.register(mmkv, listener)
|
|
21
|
+
finalization.register(mmkv, listener)
|
|
22
22
|
} else {
|
|
23
23
|
// WeakRef/FinalizationRegistry is not implemented in this engine.
|
|
24
24
|
// Just add the listener, even if it retains MMKV strong forever.
|
|
25
25
|
AppState.addEventListener('memoryWarning', () => {
|
|
26
|
-
mmkv.trim()
|
|
27
|
-
})
|
|
26
|
+
mmkv.trim()
|
|
27
|
+
})
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock MMKV instance when used in a Jest/Test environment.
|
|
5
|
+
*/
|
|
6
|
+
export function createMockMMKV(): MMKV {
|
|
7
|
+
const storage = new Map<string, string | boolean | number | ArrayBuffer>()
|
|
8
|
+
const listeners = new Set<(key: string) => void>()
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
clearAll: () => storage.clear(),
|
|
12
|
+
remove: (key) => storage.delete(key),
|
|
13
|
+
set: (key, value) => storage.set(key, value),
|
|
14
|
+
getString: (key) => {
|
|
15
|
+
const result = storage.get(key)
|
|
16
|
+
return typeof result === 'string' ? result : undefined
|
|
17
|
+
},
|
|
18
|
+
getNumber: (key) => {
|
|
19
|
+
const result = storage.get(key)
|
|
20
|
+
return typeof result === 'number' ? result : undefined
|
|
21
|
+
},
|
|
22
|
+
getBoolean: (key) => {
|
|
23
|
+
const result = storage.get(key)
|
|
24
|
+
return typeof result === 'boolean' ? result : undefined
|
|
25
|
+
},
|
|
26
|
+
getBuffer: (key) => {
|
|
27
|
+
const result = storage.get(key)
|
|
28
|
+
return result instanceof ArrayBuffer ? result : undefined
|
|
29
|
+
},
|
|
30
|
+
getAllKeys: () => Array.from(storage.keys()),
|
|
31
|
+
contains: (key) => storage.has(key),
|
|
32
|
+
recrypt: () => {
|
|
33
|
+
console.warn('Encryption is not supported in mocked MMKV instances!')
|
|
34
|
+
},
|
|
35
|
+
get size(): number {
|
|
36
|
+
return storage.size
|
|
37
|
+
},
|
|
38
|
+
isReadOnly: false,
|
|
39
|
+
trim: () => {
|
|
40
|
+
// no-op
|
|
41
|
+
},
|
|
42
|
+
name: 'MMKV',
|
|
43
|
+
dispose: () => {},
|
|
44
|
+
equals: () => {
|
|
45
|
+
return false
|
|
46
|
+
},
|
|
47
|
+
addOnValueChangedListener: (listener) => {
|
|
48
|
+
listeners.add(listener)
|
|
49
|
+
return {
|
|
50
|
+
remove: () => {
|
|
51
|
+
listeners.delete(listener)
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NitroModules } from 'react-native-nitro-modules'
|
|
2
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
3
|
+
import type { Configuration, MMKVFactory } from '../specs/MMKVFactory.nitro'
|
|
4
|
+
import type { MMKVPlatformContext } from '../specs/MMKVPlatformContext.nitro'
|
|
5
|
+
import { Platform } from 'react-native'
|
|
6
|
+
import { addMemoryWarningListener } from '../addMemoryWarningListener/addMemoryWarningListener'
|
|
7
|
+
import { isTest } from '../isTest'
|
|
8
|
+
import { createMockMMKV } from './createMMKV.mock'
|
|
9
|
+
|
|
10
|
+
let factory: MMKVFactory | undefined
|
|
11
|
+
let platformContext: MMKVPlatformContext | undefined
|
|
12
|
+
|
|
13
|
+
export function createMMKV(configuration?: Configuration): MMKV {
|
|
14
|
+
if (isTest()) {
|
|
15
|
+
// In a test environment, we mock the MMKV instance.
|
|
16
|
+
return createMockMMKV()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (platformContext == null) {
|
|
20
|
+
// Lazy-init the platform-context HybridObject
|
|
21
|
+
platformContext = NitroModules.createHybridObject<MMKVPlatformContext>(
|
|
22
|
+
'MMKVPlatformContext'
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
if (factory == null) {
|
|
26
|
+
// Lazy-init the factory HybridObject
|
|
27
|
+
factory = NitroModules.createHybridObject<MMKVFactory>('MMKVFactory')
|
|
28
|
+
const baseDirectory = platformContext.getBaseDirectory()
|
|
29
|
+
factory.initializeMMKV(baseDirectory)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Pre-parse the config
|
|
33
|
+
let config = configuration ?? { id: factory.defaultMMKVInstanceId }
|
|
34
|
+
|
|
35
|
+
if (Platform.OS === 'ios') {
|
|
36
|
+
if (config.path == null) {
|
|
37
|
+
// If the user set an App Group directory in Info.plist, let's use
|
|
38
|
+
// the App Group as a MMKV path:
|
|
39
|
+
const appGroupDirectory = platformContext.getAppGroupDirectory()
|
|
40
|
+
if (appGroupDirectory != null) {
|
|
41
|
+
config.path = appGroupDirectory
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Creates the C++ MMKV HybridObject
|
|
47
|
+
const mmkv = factory.createMMKV(config)
|
|
48
|
+
// Add a hook that trims the storage when we get a memory warning
|
|
49
|
+
addMemoryWarningListener(mmkv)
|
|
50
|
+
return mmkv
|
|
51
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Configuration
|
|
3
|
-
import { createTextEncoder } from '
|
|
1
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
2
|
+
import type { Configuration } from '../specs/MMKVFactory.nitro'
|
|
3
|
+
import { createTextEncoder } from '../web/createTextEncoder'
|
|
4
4
|
|
|
5
5
|
const canUseDOM =
|
|
6
|
-
typeof window !== 'undefined' && window.document?.createElement != null
|
|
6
|
+
typeof window !== 'undefined' && window.document?.createElement != null
|
|
7
7
|
|
|
8
8
|
const hasAccessToLocalStorage = () => {
|
|
9
9
|
try {
|
|
10
10
|
// throws ACCESS_DENIED error
|
|
11
|
-
window.localStorage
|
|
11
|
+
window.localStorage
|
|
12
12
|
|
|
13
|
-
return true
|
|
13
|
+
return true
|
|
14
14
|
} catch {
|
|
15
|
-
return false
|
|
15
|
+
return false
|
|
16
16
|
}
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
const KEY_WILDCARD = '\\'
|
|
20
|
-
const inMemoryStorage = new Map<string, string>()
|
|
19
|
+
const KEY_WILDCARD = '\\'
|
|
20
|
+
const inMemoryStorage = new Map<string, string>()
|
|
21
21
|
|
|
22
|
-
export
|
|
22
|
+
export function createMMKV(config: Configuration): MMKV {
|
|
23
23
|
if (config.encryptionKey != null) {
|
|
24
|
-
throw new Error("MMKV: 'encryptionKey' is not supported on Web!")
|
|
24
|
+
throw new Error("MMKV: 'encryptionKey' is not supported on Web!")
|
|
25
25
|
}
|
|
26
26
|
if (config.path != null) {
|
|
27
|
-
throw new Error("MMKV: 'path' is not supported on Web!")
|
|
27
|
+
throw new Error("MMKV: 'path' is not supported on Web!")
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// canUseDOM check prevents spam in Node server environments, such as Next.js server side props.
|
|
31
31
|
if (!hasAccessToLocalStorage() && canUseDOM) {
|
|
32
32
|
console.warn(
|
|
33
33
|
'MMKV: LocalStorage has been disabled. Your experience will be limited to in-memory storage!'
|
|
34
|
-
)
|
|
34
|
+
)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const storage = () => {
|
|
38
38
|
if (!canUseDOM) {
|
|
39
39
|
throw new Error(
|
|
40
40
|
'Tried to access storage on the server. Did you forget to call this in useEffect?'
|
|
41
|
-
)
|
|
41
|
+
)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if (!hasAccessToLocalStorage()) {
|
|
@@ -50,78 +50,88 @@ export const createMMKV = (config: Configuration): NativeMMKV => {
|
|
|
50
50
|
clear: () => inMemoryStorage.clear(),
|
|
51
51
|
length: inMemoryStorage.size,
|
|
52
52
|
key: (index: number) => Object.keys(inMemoryStorage).at(index) ?? null,
|
|
53
|
-
} as Storage
|
|
53
|
+
} as Storage
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const domStorage =
|
|
57
|
-
global?.localStorage ?? window?.localStorage ?? localStorage
|
|
57
|
+
global?.localStorage ?? window?.localStorage ?? localStorage
|
|
58
58
|
if (domStorage == null) {
|
|
59
|
-
throw new Error(`Could not find 'localStorage' instance!`)
|
|
59
|
+
throw new Error(`Could not find 'localStorage' instance!`)
|
|
60
60
|
}
|
|
61
|
-
return domStorage
|
|
62
|
-
}
|
|
61
|
+
return domStorage
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
const textEncoder = createTextEncoder()
|
|
64
|
+
const textEncoder = createTextEncoder()
|
|
65
|
+
const listeners = new Set<(key: string) => void>()
|
|
65
66
|
|
|
66
67
|
if (config.id.includes(KEY_WILDCARD)) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
'MMKV: `id` cannot contain the backslash character (`\\`)!'
|
|
69
|
-
);
|
|
68
|
+
throw new Error('MMKV: `id` cannot contain the backslash character (`\\`)!')
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
const keyPrefix = `${config.id}${KEY_WILDCARD}
|
|
71
|
+
const keyPrefix = `${config.id}${KEY_WILDCARD}` // mmkv.default\\
|
|
73
72
|
const prefixedKey = (key: string) => {
|
|
74
73
|
if (key.includes('\\')) {
|
|
75
74
|
throw new Error(
|
|
76
75
|
'MMKV: `key` cannot contain the backslash character (`\\`)!'
|
|
77
|
-
)
|
|
76
|
+
)
|
|
78
77
|
}
|
|
79
|
-
return `${keyPrefix}${key}
|
|
80
|
-
}
|
|
78
|
+
return `${keyPrefix}${key}`
|
|
79
|
+
}
|
|
81
80
|
|
|
82
81
|
return {
|
|
83
82
|
clearAll: () => {
|
|
84
|
-
const keys = Object.keys(storage())
|
|
83
|
+
const keys = Object.keys(storage())
|
|
85
84
|
for (const key of keys) {
|
|
86
85
|
if (key.startsWith(keyPrefix)) {
|
|
87
|
-
storage().removeItem(key)
|
|
86
|
+
storage().removeItem(key)
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
},
|
|
91
|
-
|
|
90
|
+
remove: (key) => storage().removeItem(prefixedKey(key)),
|
|
92
91
|
set: (key, value) => {
|
|
93
|
-
storage().setItem(prefixedKey(key), value.toString())
|
|
92
|
+
storage().setItem(prefixedKey(key), value.toString())
|
|
94
93
|
},
|
|
95
94
|
getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
|
|
96
95
|
getNumber: (key) => {
|
|
97
|
-
const value = storage().getItem(prefixedKey(key))
|
|
98
|
-
if (value == null) return undefined
|
|
99
|
-
return Number(value)
|
|
96
|
+
const value = storage().getItem(prefixedKey(key))
|
|
97
|
+
if (value == null) return undefined
|
|
98
|
+
return Number(value)
|
|
100
99
|
},
|
|
101
100
|
getBoolean: (key) => {
|
|
102
|
-
const value = storage().getItem(prefixedKey(key))
|
|
103
|
-
if (value == null) return undefined
|
|
104
|
-
return value === 'true'
|
|
101
|
+
const value = storage().getItem(prefixedKey(key))
|
|
102
|
+
if (value == null) return undefined
|
|
103
|
+
return value === 'true'
|
|
105
104
|
},
|
|
106
105
|
getBuffer: (key) => {
|
|
107
|
-
const value = storage().getItem(prefixedKey(key))
|
|
108
|
-
if (value == null) return undefined
|
|
109
|
-
return textEncoder.encode(value).buffer
|
|
106
|
+
const value = storage().getItem(prefixedKey(key))
|
|
107
|
+
if (value == null) return undefined
|
|
108
|
+
return textEncoder.encode(value).buffer
|
|
110
109
|
},
|
|
111
110
|
getAllKeys: () => {
|
|
112
|
-
const keys = Object.keys(storage())
|
|
111
|
+
const keys = Object.keys(storage())
|
|
113
112
|
return keys
|
|
114
113
|
.filter((key) => key.startsWith(keyPrefix))
|
|
115
|
-
.map((key) => key.slice(keyPrefix.length))
|
|
114
|
+
.map((key) => key.slice(keyPrefix.length))
|
|
116
115
|
},
|
|
117
116
|
contains: (key) => storage().getItem(prefixedKey(key)) != null,
|
|
118
117
|
recrypt: () => {
|
|
119
|
-
throw new Error('`recrypt(..)` is not supported on Web!')
|
|
118
|
+
throw new Error('`recrypt(..)` is not supported on Web!')
|
|
120
119
|
},
|
|
121
120
|
size: 0,
|
|
122
121
|
isReadOnly: false,
|
|
123
122
|
trim: () => {
|
|
124
123
|
// no-op
|
|
125
124
|
},
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
dispose: () => {},
|
|
126
|
+
equals: () => false,
|
|
127
|
+
name: 'MMKV',
|
|
128
|
+
addOnValueChangedListener: (listener) => {
|
|
129
|
+
listeners.add(listener)
|
|
130
|
+
return {
|
|
131
|
+
remove: () => {
|
|
132
|
+
listeners.delete(listener)
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
2
|
+
import { createMMKV } from './createMMKV'
|
|
3
|
+
|
|
4
|
+
let defaultInstance: MMKV | null = null
|
|
5
|
+
export function getDefaultMMKVInstance(): MMKV {
|
|
6
|
+
if (defaultInstance == null) {
|
|
7
|
+
defaultInstance = createMMKV()
|
|
8
|
+
}
|
|
9
|
+
return defaultInstance
|
|
10
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
|
|
3
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
4
|
+
|
|
5
|
+
export function createMMKVHook<
|
|
6
|
+
T extends (boolean | number | string | ArrayBufferLike) | undefined,
|
|
7
|
+
TSet extends T | undefined,
|
|
8
|
+
TSetAction extends TSet | ((current: T) => TSet),
|
|
9
|
+
>(getter: (instance: MMKV, key: string) => T) {
|
|
10
|
+
return (
|
|
11
|
+
key: string,
|
|
12
|
+
instance?: MMKV
|
|
13
|
+
): [value: T, setValue: (value: TSetAction) => void] => {
|
|
14
|
+
const mmkv = instance ?? getDefaultMMKVInstance()
|
|
15
|
+
|
|
16
|
+
const [bump, setBump] = useState(0)
|
|
17
|
+
const value = useMemo(() => {
|
|
18
|
+
// bump is here as an additional outside dependency, so this useMemo
|
|
19
|
+
// re-computes the value each time bump changes, effectively acting as a hint
|
|
20
|
+
// that the outside value (storage) has changed. setting bump refreshes this value.
|
|
21
|
+
bump
|
|
22
|
+
return getter(mmkv, key)
|
|
23
|
+
}, [mmkv, key, bump])
|
|
24
|
+
|
|
25
|
+
// update value by user set
|
|
26
|
+
const set = useCallback(
|
|
27
|
+
(v: TSetAction) => {
|
|
28
|
+
const newValue = typeof v === 'function' ? v(getter(mmkv, key)) : v
|
|
29
|
+
switch (typeof newValue) {
|
|
30
|
+
case 'number':
|
|
31
|
+
case 'string':
|
|
32
|
+
case 'boolean':
|
|
33
|
+
mmkv.set(key, newValue)
|
|
34
|
+
break
|
|
35
|
+
case 'undefined':
|
|
36
|
+
mmkv.remove(key)
|
|
37
|
+
break
|
|
38
|
+
case 'object':
|
|
39
|
+
if (newValue instanceof ArrayBuffer) {
|
|
40
|
+
mmkv.set(key, newValue)
|
|
41
|
+
break
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`MMKV: Type object (${newValue}) is not supported!`
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
default:
|
|
48
|
+
throw new Error(`MMKV: Type ${typeof newValue} is not supported!`)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[key, mmkv]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// update value if it changes somewhere else (second hook, same key)
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const listener = mmkv.addOnValueChangedListener((changedKey) => {
|
|
57
|
+
if (changedKey === key) {
|
|
58
|
+
setBump((b) => b + 1)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
return () => listener.remove()
|
|
62
|
+
}, [key, mmkv])
|
|
63
|
+
|
|
64
|
+
return [value, set]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
2
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
3
|
+
import type { Configuration } from '../specs/MMKVFactory.nitro'
|
|
4
|
+
import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
|
|
5
|
+
import { createMMKV } from '../createMMKV/createMMKV'
|
|
6
|
+
|
|
7
|
+
function isConfigurationEqual(
|
|
8
|
+
left?: Configuration,
|
|
9
|
+
right?: Configuration
|
|
10
|
+
): boolean {
|
|
11
|
+
if (left == null || right == null) return left == null && right == null
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
left.encryptionKey === right.encryptionKey &&
|
|
15
|
+
left.id === right.id &&
|
|
16
|
+
left.path === right.path &&
|
|
17
|
+
left.mode === right.mode
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Use the default, shared MMKV instance.
|
|
23
|
+
*/
|
|
24
|
+
export function useMMKV(): MMKV
|
|
25
|
+
/**
|
|
26
|
+
* Use a custom MMKV instance with the given configuration.
|
|
27
|
+
* @param configuration The configuration to initialize the MMKV instance with. Does not have to be memoized.
|
|
28
|
+
*/
|
|
29
|
+
export function useMMKV(configuration: Configuration): MMKV
|
|
30
|
+
export function useMMKV(configuration?: Configuration): MMKV {
|
|
31
|
+
const instance = useRef<MMKV>(undefined)
|
|
32
|
+
const lastConfiguration = useRef<Configuration>(undefined)
|
|
33
|
+
|
|
34
|
+
if (configuration == null) return getDefaultMMKVInstance()
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
instance.current == null ||
|
|
38
|
+
!isConfigurationEqual(lastConfiguration.current, configuration)
|
|
39
|
+
) {
|
|
40
|
+
lastConfiguration.current = configuration
|
|
41
|
+
instance.current = createMMKV(configuration)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return instance.current
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createMMKVHook } from './createMMKVHook'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Use the boolean value of the given `key` from the given MMKV storage instance.
|
|
5
|
+
*
|
|
6
|
+
* If no instance is provided, a shared default instance will be used.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const [isPremiumAccount, setIsPremiumAccount] = useMMKVBoolean("user.isPremium")
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const useMMKVBoolean = createMMKVHook((instance, key) =>
|
|
14
|
+
instance.getBoolean(key)
|
|
15
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createMMKVHook } from './createMMKVHook'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Use the buffer value (unsigned 8-bit (0-255)) of the given `key` from the given MMKV storage instance.
|
|
5
|
+
*
|
|
6
|
+
* If no instance is provided, a shared default instance will be used.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const [privateKey, setPrivateKey] = useMMKVBuffer("user.privateKey")
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const useMMKVBuffer = createMMKVHook((instance, key) =>
|
|
14
|
+
instance.getBuffer(key)
|
|
15
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { MMKV } from '../specs/MMKV.nitro'
|
|
3
|
+
import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
|
|
4
|
+
import { useMMKVListener } from './useMMKVListener'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get a list of all keys that exist in the given MMKV {@linkcode instance}.
|
|
8
|
+
* The keys update when new keys are added or removed.
|
|
9
|
+
* @param instance The instance to listen to changes to (or the default instance)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* useMMKVKeys(instance)
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function useMMKVKeys(instance?: MMKV): string[] {
|
|
17
|
+
const mmkv = instance ?? getDefaultMMKVInstance()
|
|
18
|
+
const [allKeys, setKeys] = useState<string[]>(() => mmkv.getAllKeys())
|
|
19
|
+
|
|
20
|
+
useMMKVListener((key) => {
|
|
21
|
+
// a key changed
|
|
22
|
+
setKeys((keys) => {
|
|
23
|
+
const currentlyHasKey = keys.includes(key)
|
|
24
|
+
const hasKey = mmkv.contains(key)
|
|
25
|
+
if (hasKey !== currentlyHasKey) {
|
|
26
|
+
// Re-fetch the keys from native
|
|
27
|
+
return mmkv.getAllKeys()
|
|
28
|
+
} else {
|
|
29
|
+
// We are up-to-date.
|
|
30
|
+
return keys
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}, mmkv)
|
|
34
|
+
|
|
35
|
+
return allKeys
|
|
36
|
+
}
|