react-native-mmkv 4.0.0-beta.0 → 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.
Files changed (219) hide show
  1. package/{react-native-mmkv.podspec → NitroMmkv.podspec} +17 -13
  2. package/README.md +1 -3
  3. package/android/CMakeLists.txt +31 -30
  4. package/android/build.gradle +65 -20
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  8. package/android/src/main/java/com/margelo/nitro/mmkv/HybridMMKVPlatformContext.kt +19 -0
  9. package/android/src/main/java/com/margelo/nitro/mmkv/NitroMmkvPackage.java +33 -0
  10. package/ios/HybridMMKVPlatformContext.swift +32 -0
  11. package/lib/__tests__/hooks.test.d.ts +1 -0
  12. package/lib/__tests__/hooks.test.js +66 -0
  13. package/lib/addMemoryWarningListener/addMemoryWarningListener.d.ts +2 -0
  14. package/lib/addMemoryWarningListener/addMemoryWarningListener.js +25 -0
  15. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.d.ts +2 -0
  16. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.js +3 -0
  17. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.d.ts +2 -0
  18. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.js +3 -0
  19. package/lib/createMMKV/createMMKV.d.ts +3 -0
  20. package/lib/createMMKV/createMMKV.js +40 -0
  21. package/lib/createMMKV/createMMKV.mock.d.ts +5 -0
  22. package/lib/createMMKV/createMMKV.mock.js +53 -0
  23. package/lib/createMMKV/createMMKV.web.d.ts +3 -0
  24. package/lib/createMMKV/createMMKV.web.js +117 -0
  25. package/lib/createMMKV/getDefaultMMKVInstance.d.ts +2 -0
  26. package/lib/createMMKV/getDefaultMMKVInstance.js +8 -0
  27. package/lib/hooks/createMMKVHook.d.ts +2 -0
  28. package/lib/hooks/createMMKVHook.js +49 -0
  29. package/lib/hooks/useMMKV.d.ts +11 -0
  30. package/lib/hooks/useMMKV.js +23 -0
  31. package/lib/hooks/useMMKVBoolean.d.ts +11 -0
  32. package/lib/hooks/useMMKVBoolean.js +12 -0
  33. package/lib/hooks/useMMKVBuffer.d.ts +11 -0
  34. package/lib/hooks/useMMKVBuffer.js +12 -0
  35. package/lib/hooks/useMMKVKeys.d.ts +12 -0
  36. package/lib/hooks/useMMKVKeys.js +33 -0
  37. package/lib/hooks/useMMKVListener.d.ts +15 -0
  38. package/lib/hooks/useMMKVListener.js +26 -0
  39. package/lib/hooks/useMMKVNumber.d.ts +11 -0
  40. package/lib/hooks/useMMKVNumber.js +12 -0
  41. package/lib/hooks/useMMKVObject.d.ts +17 -0
  42. package/lib/hooks/useMMKVObject.js +38 -0
  43. package/lib/hooks/useMMKVString.d.ts +11 -0
  44. package/lib/hooks/useMMKVString.js +12 -0
  45. package/lib/index.d.ts +11 -0
  46. package/lib/index.js +11 -0
  47. package/lib/isTest.d.ts +1 -0
  48. package/lib/isTest.js +7 -0
  49. package/lib/specs/MMKV.nitro.d.ts +93 -0
  50. package/lib/specs/MMKV.nitro.js +1 -0
  51. package/lib/{typescript/src/NativeMmkv.d.ts → specs/MMKVFactory.nitro.d.ts} +22 -29
  52. package/lib/specs/MMKVFactory.nitro.js +1 -0
  53. package/lib/specs/MMKVPlatformContext.nitro.d.ts +15 -0
  54. package/lib/specs/MMKVPlatformContext.nitro.js +1 -0
  55. package/lib/{typescript/src → web}/createTextEncoder.d.ts +0 -1
  56. package/lib/web/createTextEncoder.js +17 -0
  57. package/nitro.json +27 -0
  58. package/nitrogen/generated/.gitattributes +1 -0
  59. package/nitrogen/generated/android/NitroMmkv+autolinking.cmake +80 -0
  60. package/nitrogen/generated/android/NitroMmkv+autolinking.gradle +27 -0
  61. package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +55 -0
  62. package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +25 -0
  63. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +51 -0
  64. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +65 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +56 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/NitroMmkvOnLoad.kt +35 -0
  67. package/nitrogen/generated/ios/NitroMmkv+autolinking.rb +60 -0
  68. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.cpp +32 -0
  69. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +52 -0
  70. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Umbrella.hpp +44 -0
  71. package/nitrogen/generated/ios/NitroMmkvAutolinking.mm +43 -0
  72. package/nitrogen/generated/ios/NitroMmkvAutolinking.swift +25 -0
  73. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.cpp +11 -0
  74. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +81 -0
  75. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +50 -0
  76. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +135 -0
  77. package/nitrogen/generated/shared/c++/Configuration.hpp +86 -0
  78. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.cpp +23 -0
  79. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.hpp +69 -0
  80. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.cpp +22 -0
  81. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.hpp +63 -0
  82. package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +34 -0
  83. package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +83 -0
  84. package/nitrogen/generated/shared/c++/Listener.hpp +67 -0
  85. package/nitrogen/generated/shared/c++/Mode.hpp +76 -0
  86. package/package.json +75 -125
  87. package/react-native.config.js +2 -15
  88. package/src/__tests__/hooks.test.tsx +34 -34
  89. package/src/addMemoryWarningListener/addMemoryWarningListener.mock.ts +5 -0
  90. package/src/{MemoryWarningListener.ts → addMemoryWarningListener/addMemoryWarningListener.ts} +12 -12
  91. package/src/addMemoryWarningListener/addMemoryWarningListener.web.ts +5 -0
  92. package/src/createMMKV/createMMKV.mock.ts +56 -0
  93. package/src/createMMKV/createMMKV.ts +51 -0
  94. package/src/{createMMKV.web.ts → createMMKV/createMMKV.web.ts} +56 -46
  95. package/src/createMMKV/getDefaultMMKVInstance.ts +10 -0
  96. package/src/hooks/createMMKVHook.ts +66 -0
  97. package/src/hooks/useMMKV.ts +45 -0
  98. package/src/hooks/useMMKVBoolean.ts +15 -0
  99. package/src/hooks/useMMKVBuffer.ts +15 -0
  100. package/src/hooks/useMMKVKeys.ts +36 -0
  101. package/src/hooks/useMMKVListener.ts +33 -0
  102. package/src/hooks/useMMKVNumber.ts +15 -0
  103. package/src/hooks/useMMKVObject.ts +53 -0
  104. package/src/hooks/useMMKVString.ts +15 -0
  105. package/src/index.ts +15 -3
  106. package/src/{PlatformChecker.ts → isTest.ts} +2 -2
  107. package/src/specs/MMKV.nitro.ts +92 -0
  108. package/src/specs/MMKVFactory.nitro.ts +87 -0
  109. package/src/specs/MMKVPlatformContext.nitro.ts +14 -0
  110. package/src/{createTextEncoder.ts → web/createTextEncoder.ts} +7 -7
  111. package/android/src/main/cpp/AndroidLogger.cpp +0 -16
  112. package/android/src/main/java/com/mrousavy/mmkv/MmkvPackage.java +0 -44
  113. package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java +0 -26
  114. package/cpp/ManagedMMBuffer.h +0 -32
  115. package/cpp/MmkvHostObject.cpp +0 -360
  116. package/cpp/MmkvHostObject.h +0 -31
  117. package/cpp/MmkvLogger.h +0 -35
  118. package/cpp/MmkvTypes.h +0 -50
  119. package/cpp/NativeMmkvModule.cpp +0 -43
  120. package/cpp/NativeMmkvModule.h +0 -31
  121. package/ios/AppleLogger.mm +0 -16
  122. package/ios/MmkvOnLoad.mm +0 -25
  123. package/ios/MmkvPlatformContext.h +0 -19
  124. package/ios/MmkvPlatformContextModule.mm +0 -55
  125. package/lib/commonjs/MMKV.js +0 -124
  126. package/lib/commonjs/MMKV.js.map +0 -1
  127. package/lib/commonjs/MemoryWarningListener.js +0 -31
  128. package/lib/commonjs/MemoryWarningListener.js.map +0 -1
  129. package/lib/commonjs/MemoryWarningListener.web.js +0 -11
  130. package/lib/commonjs/MemoryWarningListener.web.js.map +0 -1
  131. package/lib/commonjs/ModuleNotFoundError.js +0 -75
  132. package/lib/commonjs/ModuleNotFoundError.js.map +0 -1
  133. package/lib/commonjs/NativeMmkv.js +0 -47
  134. package/lib/commonjs/NativeMmkv.js.map +0 -1
  135. package/lib/commonjs/NativeMmkvPlatformContext.js +0 -22
  136. package/lib/commonjs/NativeMmkvPlatformContext.js.map +0 -1
  137. package/lib/commonjs/PlatformChecker.js +0 -14
  138. package/lib/commonjs/PlatformChecker.js.map +0 -1
  139. package/lib/commonjs/Types.js +0 -26
  140. package/lib/commonjs/Types.js.map +0 -1
  141. package/lib/commonjs/createMMKV.js +0 -43
  142. package/lib/commonjs/createMMKV.js.map +0 -1
  143. package/lib/commonjs/createMMKV.mock.js +0 -43
  144. package/lib/commonjs/createMMKV.mock.js.map +0 -1
  145. package/lib/commonjs/createMMKV.web.js +0 -110
  146. package/lib/commonjs/createMMKV.web.js.map +0 -1
  147. package/lib/commonjs/createTextEncoder.js +0 -23
  148. package/lib/commonjs/createTextEncoder.js.map +0 -1
  149. package/lib/commonjs/hooks.js +0 -198
  150. package/lib/commonjs/hooks.js.map +0 -1
  151. package/lib/commonjs/index.js +0 -40
  152. package/lib/commonjs/index.js.map +0 -1
  153. package/lib/commonjs/package.json +0 -1
  154. package/lib/module/MMKV.js +0 -119
  155. package/lib/module/MMKV.js.map +0 -1
  156. package/lib/module/MemoryWarningListener.js +0 -27
  157. package/lib/module/MemoryWarningListener.js.map +0 -1
  158. package/lib/module/MemoryWarningListener.web.js +0 -6
  159. package/lib/module/MemoryWarningListener.web.js.map +0 -1
  160. package/lib/module/ModuleNotFoundError.js +0 -70
  161. package/lib/module/ModuleNotFoundError.js.map +0 -1
  162. package/lib/module/NativeMmkv.js +0 -45
  163. package/lib/module/NativeMmkv.js.map +0 -1
  164. package/lib/module/NativeMmkvPlatformContext.js +0 -18
  165. package/lib/module/NativeMmkvPlatformContext.js.map +0 -1
  166. package/lib/module/PlatformChecker.js +0 -10
  167. package/lib/module/PlatformChecker.js.map +0 -1
  168. package/lib/module/Types.js +0 -25
  169. package/lib/module/Types.js.map +0 -1
  170. package/lib/module/createMMKV.js +0 -38
  171. package/lib/module/createMMKV.js.map +0 -1
  172. package/lib/module/createMMKV.mock.js +0 -38
  173. package/lib/module/createMMKV.mock.js.map +0 -1
  174. package/lib/module/createMMKV.web.js +0 -105
  175. package/lib/module/createMMKV.web.js.map +0 -1
  176. package/lib/module/createTextEncoder.js +0 -19
  177. package/lib/module/createTextEncoder.js.map +0 -1
  178. package/lib/module/hooks.js +0 -189
  179. package/lib/module/hooks.js.map +0 -1
  180. package/lib/module/index.js +0 -6
  181. package/lib/module/index.js.map +0 -1
  182. package/lib/module/package.json +0 -1
  183. package/lib/typescript/src/MMKV.d.ts +0 -34
  184. package/lib/typescript/src/MMKV.d.ts.map +0 -1
  185. package/lib/typescript/src/MemoryWarningListener.d.ts +0 -3
  186. package/lib/typescript/src/MemoryWarningListener.d.ts.map +0 -1
  187. package/lib/typescript/src/MemoryWarningListener.web.d.ts +0 -3
  188. package/lib/typescript/src/MemoryWarningListener.web.d.ts.map +0 -1
  189. package/lib/typescript/src/ModuleNotFoundError.d.ts +0 -7
  190. package/lib/typescript/src/ModuleNotFoundError.d.ts.map +0 -1
  191. package/lib/typescript/src/NativeMmkv.d.ts.map +0 -1
  192. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts +0 -20
  193. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts.map +0 -1
  194. package/lib/typescript/src/PlatformChecker.d.ts +0 -2
  195. package/lib/typescript/src/PlatformChecker.d.ts.map +0 -1
  196. package/lib/typescript/src/Types.d.ts +0 -172
  197. package/lib/typescript/src/Types.d.ts.map +0 -1
  198. package/lib/typescript/src/__tests__/hooks.test.d.ts +0 -2
  199. package/lib/typescript/src/__tests__/hooks.test.d.ts.map +0 -1
  200. package/lib/typescript/src/createMMKV.d.ts +0 -3
  201. package/lib/typescript/src/createMMKV.d.ts.map +0 -1
  202. package/lib/typescript/src/createMMKV.mock.d.ts +0 -3
  203. package/lib/typescript/src/createMMKV.mock.d.ts.map +0 -1
  204. package/lib/typescript/src/createMMKV.web.d.ts +0 -3
  205. package/lib/typescript/src/createMMKV.web.d.ts.map +0 -1
  206. package/lib/typescript/src/createTextEncoder.d.ts.map +0 -1
  207. package/lib/typescript/src/hooks.d.ts +0 -86
  208. package/lib/typescript/src/hooks.d.ts.map +0 -1
  209. package/lib/typescript/src/index.d.ts +0 -4
  210. package/lib/typescript/src/index.d.ts.map +0 -1
  211. package/src/MMKV.ts +0 -142
  212. package/src/MemoryWarningListener.web.ts +0 -5
  213. package/src/ModuleNotFoundError.ts +0 -95
  214. package/src/NativeMmkv.ts +0 -118
  215. package/src/NativeMmkvPlatformContext.ts +0 -38
  216. package/src/Types.ts +0 -178
  217. package/src/createMMKV.mock.ts +0 -38
  218. package/src/createMMKV.ts +0 -42
  219. 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 { MMKV, useMMKVNumber, useMMKVString } from '..';
9
+ } from '@testing-library/react-native'
10
+ import { createMMKV, useMMKVNumber, useMMKVString } from '..'
11
11
 
12
- const mmkv = new 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
+ })
@@ -0,0 +1,5 @@
1
+ import type { MMKV } from '../specs/MMKV.nitro'
2
+
3
+ export function addMemoryWarningListener(_mmkv: MMKV): void {
4
+ // This is no-op in a mocked environment.
5
+ }
@@ -1,29 +1,29 @@
1
- import { AppState } from 'react-native';
2
- import type { NativeEventSubscription } from 'react-native';
3
- import { MMKVInterface } from './Types';
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: MMKVInterface): void {
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,5 @@
1
+ import type { MMKV } from '../specs/MMKV.nitro'
2
+
3
+ export const addMemoryWarningListener = (_mmkv: MMKV): void => {
4
+ //no-op function, there is not a web equivalent to memory warning
5
+ }
@@ -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
- /* global localStorage */
2
- import type { Configuration, NativeMMKV } from './Types';
3
- import { createTextEncoder } from './createTextEncoder';
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 const createMMKV = (config: Configuration): NativeMMKV => {
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}`; // mmkv.default\\
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
- delete: (key) => storage().removeItem(prefixedKey(key)),
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
+ }