react-native-mmkv 4.0.0-beta.1 → 4.0.0-beta.10

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 (234) hide show
  1. package/NitroMmkv.podspec +44 -0
  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/app.plugin.js +1 -0
  11. package/cpp/HybridMMKV.cpp +184 -0
  12. package/cpp/HybridMMKV.hpp +47 -0
  13. package/cpp/HybridMMKVFactory.cpp +33 -0
  14. package/cpp/HybridMMKVFactory.hpp +24 -0
  15. package/cpp/{MmkvTypes.h → MMKVTypes.hpp} +1 -1
  16. package/cpp/MMKVValueChangedListenerRegistry.cpp +58 -0
  17. package/cpp/MMKVValueChangedListenerRegistry.hpp +43 -0
  18. package/cpp/{ManagedMMBuffer.h → ManagedMMBuffer.hpp} +13 -5
  19. package/ios/HybridMMKVPlatformContext.swift +43 -0
  20. package/lib/__tests__/hooks.test.d.ts +1 -0
  21. package/lib/__tests__/hooks.test.js +69 -0
  22. package/lib/addMemoryWarningListener/addMemoryWarningListener.d.ts +2 -0
  23. package/lib/addMemoryWarningListener/addMemoryWarningListener.js +25 -0
  24. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.d.ts +2 -0
  25. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.js +3 -0
  26. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.d.ts +2 -0
  27. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.js +3 -0
  28. package/lib/createMMKV/createMMKV.d.ts +3 -0
  29. package/lib/createMMKV/createMMKV.js +40 -0
  30. package/lib/createMMKV/createMMKV.mock.d.ts +5 -0
  31. package/lib/createMMKV/createMMKV.mock.js +74 -0
  32. package/lib/createMMKV/createMMKV.web.d.ts +3 -0
  33. package/lib/createMMKV/createMMKV.web.js +117 -0
  34. package/lib/createMMKV/createMockMMKV.d.ts +5 -0
  35. package/lib/createMMKV/createMockMMKV.js +74 -0
  36. package/lib/createMMKV/getDefaultMMKVInstance.d.ts +2 -0
  37. package/lib/createMMKV/getDefaultMMKVInstance.js +8 -0
  38. package/lib/expo-plugin/withMMKV.cjs +26 -0
  39. package/lib/expo-plugin/withMMKV.d.cts +3 -0
  40. package/lib/expo-plugin/withMMKV.d.ts +3 -0
  41. package/lib/expo-plugin/withMMKV.js +17 -0
  42. package/lib/hooks/createMMKVHook.d.ts +2 -0
  43. package/lib/hooks/createMMKVHook.js +49 -0
  44. package/lib/hooks/useMMKV.d.ts +11 -0
  45. package/lib/hooks/useMMKV.js +23 -0
  46. package/lib/hooks/useMMKVBoolean.d.ts +11 -0
  47. package/lib/hooks/useMMKVBoolean.js +12 -0
  48. package/lib/hooks/useMMKVBuffer.d.ts +11 -0
  49. package/lib/hooks/useMMKVBuffer.js +12 -0
  50. package/lib/hooks/useMMKVKeys.d.ts +12 -0
  51. package/lib/hooks/useMMKVKeys.js +33 -0
  52. package/lib/hooks/useMMKVListener.d.ts +15 -0
  53. package/lib/hooks/useMMKVListener.js +26 -0
  54. package/lib/hooks/useMMKVNumber.d.ts +11 -0
  55. package/lib/hooks/useMMKVNumber.js +12 -0
  56. package/lib/hooks/useMMKVObject.d.ts +17 -0
  57. package/lib/hooks/useMMKVObject.js +38 -0
  58. package/lib/hooks/useMMKVString.d.ts +11 -0
  59. package/lib/hooks/useMMKVString.js +12 -0
  60. package/lib/index.d.ts +11 -0
  61. package/lib/index.js +11 -0
  62. package/lib/isTest.d.ts +1 -0
  63. package/lib/isTest.js +7 -0
  64. package/lib/specs/MMKV.nitro.d.ts +94 -0
  65. package/lib/specs/MMKV.nitro.js +1 -0
  66. package/lib/{typescript/src/NativeMmkv.d.ts → specs/MMKVFactory.nitro.d.ts} +26 -33
  67. package/lib/specs/MMKVFactory.nitro.js +1 -0
  68. package/lib/specs/MMKVPlatformContext.nitro.d.ts +18 -0
  69. package/lib/specs/MMKVPlatformContext.nitro.js +1 -0
  70. package/lib/{typescript/src → web}/createTextEncoder.d.ts +0 -1
  71. package/lib/web/createTextEncoder.js +17 -0
  72. package/nitro.json +28 -0
  73. package/nitrogen/generated/.gitattributes +1 -0
  74. package/nitrogen/generated/android/NitroMmkv+autolinking.cmake +80 -0
  75. package/nitrogen/generated/android/NitroMmkv+autolinking.gradle +27 -0
  76. package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +54 -0
  77. package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +25 -0
  78. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +52 -0
  79. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +65 -0
  80. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +56 -0
  81. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/NitroMmkvOnLoad.kt +35 -0
  82. package/nitrogen/generated/ios/NitroMmkv+autolinking.rb +60 -0
  83. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.cpp +32 -0
  84. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +77 -0
  85. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Umbrella.hpp +45 -0
  86. package/nitrogen/generated/ios/NitroMmkvAutolinking.mm +43 -0
  87. package/nitrogen/generated/ios/NitroMmkvAutolinking.swift +25 -0
  88. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.cpp +11 -0
  89. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +82 -0
  90. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +50 -0
  91. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +141 -0
  92. package/nitrogen/generated/shared/c++/Configuration.hpp +86 -0
  93. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.cpp +23 -0
  94. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.hpp +69 -0
  95. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.cpp +22 -0
  96. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.hpp +64 -0
  97. package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +34 -0
  98. package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +83 -0
  99. package/nitrogen/generated/shared/c++/Listener.hpp +67 -0
  100. package/nitrogen/generated/shared/c++/Mode.hpp +76 -0
  101. package/package.json +74 -122
  102. package/react-native.config.js +2 -15
  103. package/src/__tests__/hooks.test.tsx +39 -34
  104. package/src/addMemoryWarningListener/addMemoryWarningListener.mock.ts +5 -0
  105. package/src/{MemoryWarningListener.ts → addMemoryWarningListener/addMemoryWarningListener.ts} +12 -12
  106. package/src/addMemoryWarningListener/addMemoryWarningListener.web.ts +5 -0
  107. package/src/createMMKV/createMMKV.ts +51 -0
  108. package/src/{createMMKV.web.ts → createMMKV/createMMKV.web.ts} +58 -46
  109. package/src/createMMKV/createMockMMKV.ts +78 -0
  110. package/src/createMMKV/getDefaultMMKVInstance.ts +10 -0
  111. package/src/expo-plugin/withMMKV.cts +31 -0
  112. package/src/hooks/createMMKVHook.ts +66 -0
  113. package/src/hooks/useMMKV.ts +45 -0
  114. package/src/hooks/useMMKVBoolean.ts +15 -0
  115. package/src/hooks/useMMKVBuffer.ts +15 -0
  116. package/src/hooks/useMMKVKeys.ts +36 -0
  117. package/src/hooks/useMMKVListener.ts +33 -0
  118. package/src/hooks/useMMKVNumber.ts +15 -0
  119. package/src/hooks/useMMKVObject.ts +53 -0
  120. package/src/hooks/useMMKVString.ts +15 -0
  121. package/src/index.ts +15 -3
  122. package/src/{PlatformChecker.ts → isTest.ts} +2 -2
  123. package/src/specs/MMKV.nitro.ts +93 -0
  124. package/src/specs/MMKVFactory.nitro.ts +87 -0
  125. package/src/specs/MMKVPlatformContext.nitro.ts +17 -0
  126. package/src/{createTextEncoder.ts → web/createTextEncoder.ts} +7 -7
  127. package/android/src/main/cpp/AndroidLogger.cpp +0 -16
  128. package/android/src/main/java/com/mrousavy/mmkv/MmkvPackage.java +0 -44
  129. package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java +0 -26
  130. package/cpp/MmkvHostObject.cpp +0 -360
  131. package/cpp/MmkvHostObject.h +0 -31
  132. package/cpp/MmkvLogger.h +0 -35
  133. package/cpp/NativeMmkvModule.cpp +0 -43
  134. package/cpp/NativeMmkvModule.h +0 -31
  135. package/ios/AppleLogger.mm +0 -16
  136. package/ios/MmkvOnLoad.mm +0 -25
  137. package/ios/MmkvPlatformContext.h +0 -19
  138. package/ios/MmkvPlatformContextModule.mm +0 -55
  139. package/lib/commonjs/MMKV.js +0 -124
  140. package/lib/commonjs/MMKV.js.map +0 -1
  141. package/lib/commonjs/MemoryWarningListener.js +0 -31
  142. package/lib/commonjs/MemoryWarningListener.js.map +0 -1
  143. package/lib/commonjs/MemoryWarningListener.web.js +0 -11
  144. package/lib/commonjs/MemoryWarningListener.web.js.map +0 -1
  145. package/lib/commonjs/ModuleNotFoundError.js +0 -75
  146. package/lib/commonjs/ModuleNotFoundError.js.map +0 -1
  147. package/lib/commonjs/NativeMmkv.js +0 -47
  148. package/lib/commonjs/NativeMmkv.js.map +0 -1
  149. package/lib/commonjs/NativeMmkvPlatformContext.js +0 -22
  150. package/lib/commonjs/NativeMmkvPlatformContext.js.map +0 -1
  151. package/lib/commonjs/PlatformChecker.js +0 -14
  152. package/lib/commonjs/PlatformChecker.js.map +0 -1
  153. package/lib/commonjs/Types.js +0 -26
  154. package/lib/commonjs/Types.js.map +0 -1
  155. package/lib/commonjs/createMMKV.js +0 -43
  156. package/lib/commonjs/createMMKV.js.map +0 -1
  157. package/lib/commonjs/createMMKV.mock.js +0 -43
  158. package/lib/commonjs/createMMKV.mock.js.map +0 -1
  159. package/lib/commonjs/createMMKV.web.js +0 -110
  160. package/lib/commonjs/createMMKV.web.js.map +0 -1
  161. package/lib/commonjs/createTextEncoder.js +0 -23
  162. package/lib/commonjs/createTextEncoder.js.map +0 -1
  163. package/lib/commonjs/hooks.js +0 -198
  164. package/lib/commonjs/hooks.js.map +0 -1
  165. package/lib/commonjs/index.js +0 -40
  166. package/lib/commonjs/index.js.map +0 -1
  167. package/lib/commonjs/package.json +0 -1
  168. package/lib/module/MMKV.js +0 -119
  169. package/lib/module/MMKV.js.map +0 -1
  170. package/lib/module/MemoryWarningListener.js +0 -27
  171. package/lib/module/MemoryWarningListener.js.map +0 -1
  172. package/lib/module/MemoryWarningListener.web.js +0 -6
  173. package/lib/module/MemoryWarningListener.web.js.map +0 -1
  174. package/lib/module/ModuleNotFoundError.js +0 -70
  175. package/lib/module/ModuleNotFoundError.js.map +0 -1
  176. package/lib/module/NativeMmkv.js +0 -45
  177. package/lib/module/NativeMmkv.js.map +0 -1
  178. package/lib/module/NativeMmkvPlatformContext.js +0 -18
  179. package/lib/module/NativeMmkvPlatformContext.js.map +0 -1
  180. package/lib/module/PlatformChecker.js +0 -10
  181. package/lib/module/PlatformChecker.js.map +0 -1
  182. package/lib/module/Types.js +0 -25
  183. package/lib/module/Types.js.map +0 -1
  184. package/lib/module/createMMKV.js +0 -38
  185. package/lib/module/createMMKV.js.map +0 -1
  186. package/lib/module/createMMKV.mock.js +0 -38
  187. package/lib/module/createMMKV.mock.js.map +0 -1
  188. package/lib/module/createMMKV.web.js +0 -105
  189. package/lib/module/createMMKV.web.js.map +0 -1
  190. package/lib/module/createTextEncoder.js +0 -19
  191. package/lib/module/createTextEncoder.js.map +0 -1
  192. package/lib/module/hooks.js +0 -189
  193. package/lib/module/hooks.js.map +0 -1
  194. package/lib/module/index.js +0 -6
  195. package/lib/module/index.js.map +0 -1
  196. package/lib/module/package.json +0 -1
  197. package/lib/typescript/src/MMKV.d.ts +0 -34
  198. package/lib/typescript/src/MMKV.d.ts.map +0 -1
  199. package/lib/typescript/src/MemoryWarningListener.d.ts +0 -3
  200. package/lib/typescript/src/MemoryWarningListener.d.ts.map +0 -1
  201. package/lib/typescript/src/MemoryWarningListener.web.d.ts +0 -3
  202. package/lib/typescript/src/MemoryWarningListener.web.d.ts.map +0 -1
  203. package/lib/typescript/src/ModuleNotFoundError.d.ts +0 -7
  204. package/lib/typescript/src/ModuleNotFoundError.d.ts.map +0 -1
  205. package/lib/typescript/src/NativeMmkv.d.ts.map +0 -1
  206. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts +0 -20
  207. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts.map +0 -1
  208. package/lib/typescript/src/PlatformChecker.d.ts +0 -2
  209. package/lib/typescript/src/PlatformChecker.d.ts.map +0 -1
  210. package/lib/typescript/src/Types.d.ts +0 -172
  211. package/lib/typescript/src/Types.d.ts.map +0 -1
  212. package/lib/typescript/src/__tests__/hooks.test.d.ts +0 -2
  213. package/lib/typescript/src/__tests__/hooks.test.d.ts.map +0 -1
  214. package/lib/typescript/src/createMMKV.d.ts +0 -3
  215. package/lib/typescript/src/createMMKV.d.ts.map +0 -1
  216. package/lib/typescript/src/createMMKV.mock.d.ts +0 -3
  217. package/lib/typescript/src/createMMKV.mock.d.ts.map +0 -1
  218. package/lib/typescript/src/createMMKV.web.d.ts +0 -3
  219. package/lib/typescript/src/createMMKV.web.d.ts.map +0 -1
  220. package/lib/typescript/src/createTextEncoder.d.ts.map +0 -1
  221. package/lib/typescript/src/hooks.d.ts +0 -86
  222. package/lib/typescript/src/hooks.d.ts.map +0 -1
  223. package/lib/typescript/src/index.d.ts +0 -4
  224. package/lib/typescript/src/index.d.ts.map +0 -1
  225. package/react-native-mmkv.podspec +0 -32
  226. package/src/MMKV.ts +0 -142
  227. package/src/MemoryWarningListener.web.ts +0 -5
  228. package/src/ModuleNotFoundError.ts +0 -95
  229. package/src/NativeMmkv.ts +0 -118
  230. package/src/NativeMmkvPlatformContext.ts +0 -38
  231. package/src/Types.ts +0 -178
  232. package/src/createMMKV.mock.ts +0 -38
  233. package/src/createMMKV.ts +0 -42
  234. package/src/hooks.ts +0 -247
@@ -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,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 './createMockMMKV'
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,46 @@
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(
23
+ config: Configuration = { id: 'mmkv.default' }
24
+ ): MMKV {
23
25
  if (config.encryptionKey != null) {
24
- throw new Error("MMKV: 'encryptionKey' is not supported on Web!");
26
+ throw new Error("MMKV: 'encryptionKey' is not supported on Web!")
25
27
  }
26
28
  if (config.path != null) {
27
- throw new Error("MMKV: 'path' is not supported on Web!");
29
+ throw new Error("MMKV: 'path' is not supported on Web!")
28
30
  }
29
31
 
30
32
  // canUseDOM check prevents spam in Node server environments, such as Next.js server side props.
31
33
  if (!hasAccessToLocalStorage() && canUseDOM) {
32
34
  console.warn(
33
35
  'MMKV: LocalStorage has been disabled. Your experience will be limited to in-memory storage!'
34
- );
36
+ )
35
37
  }
36
38
 
37
39
  const storage = () => {
38
40
  if (!canUseDOM) {
39
41
  throw new Error(
40
42
  'Tried to access storage on the server. Did you forget to call this in useEffect?'
41
- );
43
+ )
42
44
  }
43
45
 
44
46
  if (!hasAccessToLocalStorage()) {
@@ -50,78 +52,88 @@ export const createMMKV = (config: Configuration): NativeMMKV => {
50
52
  clear: () => inMemoryStorage.clear(),
51
53
  length: inMemoryStorage.size,
52
54
  key: (index: number) => Object.keys(inMemoryStorage).at(index) ?? null,
53
- } as Storage;
55
+ } as Storage
54
56
  }
55
57
 
56
58
  const domStorage =
57
- global?.localStorage ?? window?.localStorage ?? localStorage;
59
+ global?.localStorage ?? window?.localStorage ?? localStorage
58
60
  if (domStorage == null) {
59
- throw new Error(`Could not find 'localStorage' instance!`);
61
+ throw new Error(`Could not find 'localStorage' instance!`)
60
62
  }
61
- return domStorage;
62
- };
63
+ return domStorage
64
+ }
63
65
 
64
- const textEncoder = createTextEncoder();
66
+ const textEncoder = createTextEncoder()
67
+ const listeners = new Set<(key: string) => void>()
65
68
 
66
69
  if (config.id.includes(KEY_WILDCARD)) {
67
- throw new Error(
68
- 'MMKV: `id` cannot contain the backslash character (`\\`)!'
69
- );
70
+ throw new Error('MMKV: `id` cannot contain the backslash character (`\\`)!')
70
71
  }
71
72
 
72
- const keyPrefix = `${config.id}${KEY_WILDCARD}`; // mmkv.default\\
73
+ const keyPrefix = `${config.id}${KEY_WILDCARD}` // mmkv.default\\
73
74
  const prefixedKey = (key: string) => {
74
75
  if (key.includes('\\')) {
75
76
  throw new Error(
76
77
  'MMKV: `key` cannot contain the backslash character (`\\`)!'
77
- );
78
+ )
78
79
  }
79
- return `${keyPrefix}${key}`;
80
- };
80
+ return `${keyPrefix}${key}`
81
+ }
81
82
 
82
83
  return {
83
84
  clearAll: () => {
84
- const keys = Object.keys(storage());
85
+ const keys = Object.keys(storage())
85
86
  for (const key of keys) {
86
87
  if (key.startsWith(keyPrefix)) {
87
- storage().removeItem(key);
88
+ storage().removeItem(key)
88
89
  }
89
90
  }
90
91
  },
91
- delete: (key) => storage().removeItem(prefixedKey(key)),
92
+ remove: (key) => storage().removeItem(prefixedKey(key)) ?? false,
92
93
  set: (key, value) => {
93
- storage().setItem(prefixedKey(key), value.toString());
94
+ storage().setItem(prefixedKey(key), value.toString())
94
95
  },
95
96
  getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
96
97
  getNumber: (key) => {
97
- const value = storage().getItem(prefixedKey(key));
98
- if (value == null) return undefined;
99
- return Number(value);
98
+ const value = storage().getItem(prefixedKey(key))
99
+ if (value == null) return undefined
100
+ return Number(value)
100
101
  },
101
102
  getBoolean: (key) => {
102
- const value = storage().getItem(prefixedKey(key));
103
- if (value == null) return undefined;
104
- return value === 'true';
103
+ const value = storage().getItem(prefixedKey(key))
104
+ if (value == null) return undefined
105
+ return value === 'true'
105
106
  },
106
107
  getBuffer: (key) => {
107
- const value = storage().getItem(prefixedKey(key));
108
- if (value == null) return undefined;
109
- return textEncoder.encode(value).buffer;
108
+ const value = storage().getItem(prefixedKey(key))
109
+ if (value == null) return undefined
110
+ return textEncoder.encode(value).buffer
110
111
  },
111
112
  getAllKeys: () => {
112
- const keys = Object.keys(storage());
113
+ const keys = Object.keys(storage())
113
114
  return keys
114
115
  .filter((key) => key.startsWith(keyPrefix))
115
- .map((key) => key.slice(keyPrefix.length));
116
+ .map((key) => key.slice(keyPrefix.length))
116
117
  },
117
118
  contains: (key) => storage().getItem(prefixedKey(key)) != null,
118
119
  recrypt: () => {
119
- throw new Error('`recrypt(..)` is not supported on Web!');
120
+ throw new Error('`recrypt(..)` is not supported on Web!')
120
121
  },
121
122
  size: 0,
122
123
  isReadOnly: false,
123
124
  trim: () => {
124
125
  // no-op
125
126
  },
126
- };
127
- };
127
+ dispose: () => {},
128
+ equals: () => false,
129
+ name: 'MMKV',
130
+ addOnValueChangedListener: (listener) => {
131
+ listeners.add(listener)
132
+ return {
133
+ remove: () => {
134
+ listeners.delete(listener)
135
+ },
136
+ }
137
+ },
138
+ }
139
+ }
@@ -0,0 +1,78 @@
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
+ const notifyListeners = (key: string) => {
11
+ listeners.forEach((listener) => {
12
+ listener(key)
13
+ })
14
+ }
15
+
16
+ return {
17
+ clearAll: () => {
18
+ const keysBefore = storage.keys()
19
+ storage.clear()
20
+ // Notify all listeners for all keys that were cleared
21
+ for (const key of keysBefore) {
22
+ notifyListeners(key)
23
+ }
24
+ },
25
+ remove: (key) => {
26
+ const deleted = storage.delete(key)
27
+ if (deleted) {
28
+ notifyListeners(key)
29
+ }
30
+ return deleted
31
+ },
32
+ set: (key, value) => {
33
+ storage.set(key, value)
34
+ notifyListeners(key)
35
+ },
36
+ getString: (key) => {
37
+ const result = storage.get(key)
38
+ return typeof result === 'string' ? result : undefined
39
+ },
40
+ getNumber: (key) => {
41
+ const result = storage.get(key)
42
+ return typeof result === 'number' ? result : undefined
43
+ },
44
+ getBoolean: (key) => {
45
+ const result = storage.get(key)
46
+ return typeof result === 'boolean' ? result : undefined
47
+ },
48
+ getBuffer: (key) => {
49
+ const result = storage.get(key)
50
+ return result instanceof ArrayBuffer ? result : undefined
51
+ },
52
+ getAllKeys: () => Array.from(storage.keys()),
53
+ contains: (key) => storage.has(key),
54
+ recrypt: () => {
55
+ console.warn('Encryption is not supported in mocked MMKV instances!')
56
+ },
57
+ get size(): number {
58
+ return storage.size
59
+ },
60
+ isReadOnly: false,
61
+ trim: () => {
62
+ // no-op
63
+ },
64
+ name: 'MMKV',
65
+ dispose: () => {},
66
+ equals: () => {
67
+ return false
68
+ },
69
+ addOnValueChangedListener: (listener) => {
70
+ listeners.add(listener)
71
+ return {
72
+ remove: () => {
73
+ listeners.delete(listener)
74
+ },
75
+ }
76
+ },
77
+ }
78
+ }
@@ -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,31 @@
1
+ import type { ConfigPlugin, ExportedConfigWithProps } from '@expo/config-plugins'
2
+ const { createRunOncePlugin, withGradleProperties } = require('@expo/config-plugins')
3
+ import type { Properties } from '@expo/config-plugins/build/android'
4
+
5
+ const pkg = require('../../package.json')
6
+
7
+ const withMMKV: ConfigPlugin<{}> = (config) => {
8
+ // remove 32-bit architectures from gradle.properties
9
+ return withGradleProperties(config, (cfg: ExportedConfigWithProps<Properties.PropertiesItem[]>) => {
10
+ // Define the wanted property
11
+ const property = {
12
+ type: 'property',
13
+ key: 'reactNativeArchitectures',
14
+ value: 'arm64-v8a,x86_64',
15
+ } as const
16
+ // If it exists, update its value
17
+ const index = cfg.modResults.findIndex(
18
+ (p) => p.type === 'property' && p.key === property.key
19
+ )
20
+ if (index !== -1) {
21
+ // Overwrite it
22
+ cfg.modResults[index] = property
23
+ } else {
24
+ // Append it
25
+ cfg.modResults.push(property)
26
+ }
27
+ return cfg
28
+ })
29
+ }
30
+
31
+ export = createRunOncePlugin(withMMKV, pkg.name, pkg.version) as ConfigPlugin<{}>
@@ -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
+ }
@@ -0,0 +1,33 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
4
+
5
+ /**
6
+ * Listen for changes in the given MMKV storage instance.
7
+ * If no instance is passed, the default instance will be used.
8
+ * @param valueChangedListener The function to call whenever a value inside the storage instance changes
9
+ * @param instance The instance to listen to changes to (or the default instance)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * useMMKVListener((key) => {
14
+ * console.log(`Value for "${key}" changed!`)
15
+ * })
16
+ * ```
17
+ */
18
+ export function useMMKVListener(
19
+ valueChangedListener: (key: string) => void,
20
+ instance?: MMKV
21
+ ): void {
22
+ const ref = useRef(valueChangedListener)
23
+ ref.current = valueChangedListener
24
+
25
+ const mmkv = instance ?? getDefaultMMKVInstance()
26
+
27
+ useEffect(() => {
28
+ const listener = mmkv.addOnValueChangedListener((changedKey) => {
29
+ ref.current(changedKey)
30
+ })
31
+ return () => listener.remove()
32
+ }, [mmkv])
33
+ }
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the number 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 [age, setAge] = useMMKVNumber("user.age")
11
+ * ```
12
+ */
13
+ export const useMMKVNumber = createMMKVHook((instance, key) =>
14
+ instance.getNumber(key)
15
+ )
@@ -0,0 +1,53 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import { useMMKVString } from './useMMKVString'
4
+
5
+ /**
6
+ * Use an object value of the given `key` from the given MMKV storage instance.
7
+ *
8
+ * If no instance is provided, a shared default instance will be used.
9
+ *
10
+ * The object will be serialized using `JSON`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const [user, setUser] = useMMKVObject<User>("user")
15
+ * ```
16
+ */
17
+ export function useMMKVObject<T>(
18
+ key: string,
19
+ instance?: MMKV
20
+ ): [
21
+ value: T | undefined,
22
+ setValue: (
23
+ value: T | undefined | ((prevValue: T | undefined) => T | undefined)
24
+ ) => void,
25
+ ] {
26
+ const [json, setJson] = useMMKVString(key, instance)
27
+
28
+ const value = useMemo(() => {
29
+ if (json == null) return undefined
30
+ return JSON.parse(json) as T
31
+ }, [json])
32
+
33
+ const setValue = useCallback(
34
+ (v: (T | undefined) | ((prev: T | undefined) => T | undefined)) => {
35
+ if (v instanceof Function) {
36
+ setJson((currentJson) => {
37
+ const currentValue =
38
+ currentJson != null ? (JSON.parse(currentJson) as T) : undefined
39
+ const newValue = v(currentValue)
40
+ // Store the Object as a serialized Value or clear the value
41
+ return newValue != null ? JSON.stringify(newValue) : undefined
42
+ })
43
+ } else {
44
+ // Store the Object as a serialized Value or clear the value
45
+ const newValue = v != null ? JSON.stringify(v) : undefined
46
+ setJson(newValue)
47
+ }
48
+ },
49
+ [setJson]
50
+ )
51
+
52
+ return [value, setValue]
53
+ }
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the string 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 [username, setUsername] = useMMKVString("user.name")
11
+ * ```
12
+ */
13
+ export const useMMKVString = createMMKVHook((instance, key) =>
14
+ instance.getString(key)
15
+ )