react-native-nitro-storage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +447 -0
- package/android/CMakeLists.txt +33 -0
- package/android/build.gradle +96 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +49 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +68 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +94 -0
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +27 -0
- package/app.plugin.js +106 -0
- package/cpp/bindings/HybridStorage.cpp +144 -0
- package/cpp/bindings/HybridStorage.hpp +52 -0
- package/cpp/core/NativeStorageAdapter.hpp +21 -0
- package/ios/IOSStorageAdapterCpp.hpp +21 -0
- package/ios/IOSStorageAdapterCpp.mm +127 -0
- package/lib/commonjs/Storage.nitro.js +13 -0
- package/lib/commonjs/Storage.nitro.js.map +1 -0
- package/lib/commonjs/index.js +88 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/Storage.nitro.js +9 -0
- package/lib/module/Storage.nitro.js.map +1 -0
- package/lib/module/index.js +77 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +16 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +19 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/nitro.json +15 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroStorage+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroStorage+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroStorageOnLoad.cpp +44 -0
- package/nitrogen/generated/android/NitroStorageOnLoad.hpp +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitrostorage/NitroStorageOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NitroStorage+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NitroStorageAutolinking.mm +35 -0
- package/nitrogen/generated/ios/NitroStorageAutolinking.swift +12 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +67 -0
- package/package.json +117 -0
- package/react-native-nitro-storage.podspec +36 -0
- package/src/Storage.nitro.ts +17 -0
- package/src/index.ts +113 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 João Paulo C. Marra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
# react-native-nitro-storage 🗄️
|
|
2
|
+
|
|
3
|
+
> **The fastest, most complete storage solution for React Native.** Replace Zustand, MMKV, AsyncStorage, and Expo Secure Store with one lightning-fast, type-safe library.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-nitro-storage)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nitro.margelo.com)
|
|
8
|
+
|
|
9
|
+
**react-native-nitro-storage** unifies **Memory** (global state), **Disk** (persistence), and **Secure** (keychain) storage into a single, blazing-fast C++ library built with [Nitro Modules](https://nitro.margelo.com). All operations are **100% synchronous** via JSI—no promises, no bridge, no lag.
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="./readme/ios.png" alt="iOS Benchmark" height="640" />
|
|
13
|
+
<img src="./readme/android.png" alt="Android Benchmark" height="640" />
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<em>Real-world performance: 1,000 operations in milliseconds</em>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ⚡ Why Nitro Storage?
|
|
23
|
+
|
|
24
|
+
### **One Library, Three Storage Types**
|
|
25
|
+
|
|
26
|
+
Stop juggling multiple packages. Get memory state, disk persistence, and secure storage in one unified API.
|
|
27
|
+
|
|
28
|
+
### **Truly Synchronous**
|
|
29
|
+
|
|
30
|
+
Every operation—read, write, delete—executes instantly. No `await`, no `.then()`, no bridge overhead.
|
|
31
|
+
|
|
32
|
+
### **Jotai-Style Atoms**
|
|
33
|
+
|
|
34
|
+
Familiar, elegant API with `createStorageItem` and `useStorage`. Works inside and outside React components.
|
|
35
|
+
|
|
36
|
+
### **Production-Ready**
|
|
37
|
+
|
|
38
|
+
Thread-safe C++ core, comprehensive test coverage, and battle-tested on iOS and Android.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📦 Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install react-native-nitro-storage react-native-nitro-modules
|
|
46
|
+
# or
|
|
47
|
+
yarn add react-native-nitro-storage react-native-nitro-modules
|
|
48
|
+
# or
|
|
49
|
+
bun add react-native-nitro-storage react-native-nitro-modules
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### For Expo Projects
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx expo install react-native-nitro-storage react-native-nitro-modules
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Add the plugin to your `app.json` or `app.config.js`:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"expo": {
|
|
63
|
+
"plugins": ["react-native-nitro-storage"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx expo prebuild
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The config plugin automatically handles Android initialization. No manual setup required!
|
|
75
|
+
|
|
76
|
+
### For Bare React Native Projects
|
|
77
|
+
|
|
78
|
+
**iOS:**
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd ios && pod install
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Android:**
|
|
85
|
+
|
|
86
|
+
Add initialization to your `MainApplication.kt` (or `.java`):
|
|
87
|
+
|
|
88
|
+
```kotlin
|
|
89
|
+
import com.nitrostorage.AndroidStorageAdapter
|
|
90
|
+
|
|
91
|
+
class MainApplication : Application() {
|
|
92
|
+
override fun onCreate() {
|
|
93
|
+
super.onCreate()
|
|
94
|
+
AndroidStorageAdapter.init(this)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🚀 Quick Start
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import {
|
|
105
|
+
createStorageItem,
|
|
106
|
+
useStorage,
|
|
107
|
+
StorageScope,
|
|
108
|
+
} from "react-native-nitro-storage";
|
|
109
|
+
|
|
110
|
+
// Create a storage atom
|
|
111
|
+
const counterAtom = createStorageItem({
|
|
112
|
+
key: "counter",
|
|
113
|
+
scope: StorageScope.Memory,
|
|
114
|
+
defaultValue: 0,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Use in React
|
|
118
|
+
function Counter() {
|
|
119
|
+
const [count, setCount] = useStorage(counterAtom);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<View>
|
|
123
|
+
<Text>{count}</Text>
|
|
124
|
+
<Button title="+" onPress={() => setCount(count + 1)} />
|
|
125
|
+
</View>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Use outside React
|
|
130
|
+
counterAtom.set(42);
|
|
131
|
+
const value = counterAtom.get(); // 42, instantly
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 💾 Storage Scopes
|
|
137
|
+
|
|
138
|
+
### **Memory Storage**
|
|
139
|
+
|
|
140
|
+
_Replaces: Zustand, Jotai, Redux_
|
|
141
|
+
|
|
142
|
+
Fast, in-memory state. Perfect for global app state that doesn't need persistence.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const userAtom = createStorageItem<User | null>({
|
|
146
|
+
key: "current-user",
|
|
147
|
+
scope: StorageScope.Memory,
|
|
148
|
+
defaultValue: null,
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Performance:** < 0.001ms per operation
|
|
153
|
+
|
|
154
|
+
### **Disk Storage**
|
|
155
|
+
|
|
156
|
+
_Replaces: MMKV, AsyncStorage_
|
|
157
|
+
|
|
158
|
+
Persisted to disk. Survives app restarts.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const settingsAtom = createStorageItem({
|
|
162
|
+
key: "app-settings",
|
|
163
|
+
scope: StorageScope.Disk,
|
|
164
|
+
defaultValue: { theme: "dark", notifications: true },
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Performance:** ~1-2ms per operation
|
|
169
|
+
**Storage:** UserDefaults (iOS), SharedPreferences (Android)
|
|
170
|
+
|
|
171
|
+
### **Secure Storage**
|
|
172
|
+
|
|
173
|
+
_Replaces: Expo Secure Store, react-native-keychain_
|
|
174
|
+
|
|
175
|
+
Encrypted storage for sensitive data like auth tokens.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const tokenAtom = createStorageItem<string | undefined>({
|
|
179
|
+
key: "auth-token",
|
|
180
|
+
scope: StorageScope.Secure,
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Performance:** ~2-5ms per operation
|
|
185
|
+
**Encryption:** Keychain (iOS), EncryptedSharedPreferences with AES256-GCM (Android)
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 🎯 Advanced Usage
|
|
190
|
+
|
|
191
|
+
### TypeScript Best Practices
|
|
192
|
+
|
|
193
|
+
**Nullable Types:**
|
|
194
|
+
Use explicit generics for nullable values. `defaultValue` is optional and defaults to `undefined`.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// ✅ Clean - explicit generic
|
|
198
|
+
const userAtom = createStorageItem<User | null>({
|
|
199
|
+
key: "current-user",
|
|
200
|
+
scope: StorageScope.Memory,
|
|
201
|
+
defaultValue: null,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// ✅ Optional value - no defaultValue needed
|
|
205
|
+
const tokenAtom = createStorageItem<string | undefined>({
|
|
206
|
+
key: "auth-token",
|
|
207
|
+
scope: StorageScope.Secure,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ❌ Avoid - type assertion
|
|
211
|
+
const userAtom = createStorageItem({
|
|
212
|
+
key: "current-user",
|
|
213
|
+
scope: StorageScope.Memory,
|
|
214
|
+
defaultValue: null as User | null, // Not necessary
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Non-nullable Types:**
|
|
219
|
+
For required values, just specify the default.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const counterAtom = createStorageItem({
|
|
223
|
+
key: "counter",
|
|
224
|
+
scope: StorageScope.Memory,
|
|
225
|
+
defaultValue: 0, // Type inferred as number
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Custom Serialization
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const dateAtom = createStorageItem({
|
|
233
|
+
key: "last-login",
|
|
234
|
+
scope: StorageScope.Disk,
|
|
235
|
+
defaultValue: new Date(),
|
|
236
|
+
serialize: (date) => date.toISOString(),
|
|
237
|
+
deserialize: (str) => new Date(str),
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Complex Objects
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
interface User {
|
|
245
|
+
id: string;
|
|
246
|
+
name: string;
|
|
247
|
+
preferences: {
|
|
248
|
+
theme: "light" | "dark";
|
|
249
|
+
language: string;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const userAtom = createStorageItem<User>({
|
|
254
|
+
key: "user",
|
|
255
|
+
scope: StorageScope.Disk,
|
|
256
|
+
defaultValue: {
|
|
257
|
+
id: "",
|
|
258
|
+
name: "",
|
|
259
|
+
preferences: { theme: "dark", language: "en" },
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// TypeScript knows the exact shape
|
|
264
|
+
const user = userAtom.get();
|
|
265
|
+
console.log(user.preferences.theme); // ✅ Type-safe
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Direct Access (Outside React)
|
|
269
|
+
|
|
270
|
+
Perfect for API interceptors, middleware, or anywhere you need storage without React.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// In an API interceptor
|
|
274
|
+
axios.interceptors.request.use((config) => {
|
|
275
|
+
const token = tokenAtom.get();
|
|
276
|
+
if (token) {
|
|
277
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
278
|
+
}
|
|
279
|
+
return config;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// In a Redux middleware
|
|
283
|
+
const authMiddleware = (store) => (next) => (action) => {
|
|
284
|
+
if (action.type === "AUTH_SUCCESS") {
|
|
285
|
+
tokenAtom.set(action.payload.token);
|
|
286
|
+
}
|
|
287
|
+
return next(action);
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Manual Subscriptions
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
const unsubscribe = counterAtom.subscribe(() => {
|
|
295
|
+
console.log("Counter changed:", counterAtom.get());
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Clean up
|
|
299
|
+
unsubscribe();
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 📊 Performance Benchmarks
|
|
305
|
+
|
|
306
|
+
All operations are **100% synchronous** via JSI—no promises, no bridge, no lag.
|
|
307
|
+
|
|
308
|
+
**Performance Metrics (1,000 operations per storage type):**
|
|
309
|
+
|
|
310
|
+
| Storage Type | Write | Read | Avg/op |
|
|
311
|
+
| ------------ | ----- | ----- | ------------ |
|
|
312
|
+
| Memory | 0.5ms | 0.3ms | **0.0008ms** |
|
|
313
|
+
| Disk | 45ms | 38ms | **0.083ms** |
|
|
314
|
+
| Secure | 120ms | 95ms | **0.215ms** |
|
|
315
|
+
|
|
316
|
+
Run the benchmark yourself:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
cd apps/example
|
|
320
|
+
npm run ios # or npm run android
|
|
321
|
+
# Navigate to the "Benchmark" tab
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 🎯 API Reference
|
|
327
|
+
|
|
328
|
+
### `createStorageItem<T>(config)`
|
|
329
|
+
|
|
330
|
+
Creates a storage atom.
|
|
331
|
+
|
|
332
|
+
**Parameters:**
|
|
333
|
+
|
|
334
|
+
- `key: string` - Unique identifier
|
|
335
|
+
- `scope: StorageScope` - Memory, Disk, or Secure
|
|
336
|
+
- `defaultValue: T` - Default value
|
|
337
|
+
- `serialize?: (value: T) => string` - Custom serializer (default: JSON.stringify)
|
|
338
|
+
- `deserialize?: (value: string) => T` - Custom deserializer (default: JSON.parse)
|
|
339
|
+
|
|
340
|
+
**Returns:** `StorageItem<T>`
|
|
341
|
+
|
|
342
|
+
### `StorageItem<T>`
|
|
343
|
+
|
|
344
|
+
**Methods:**
|
|
345
|
+
|
|
346
|
+
- `get(): T` - Get current value (synchronous)
|
|
347
|
+
- `set(value: T): void` - Set new value (synchronous)
|
|
348
|
+
- `delete(): void` - Remove value (synchronous)
|
|
349
|
+
- `subscribe(callback: () => void): () => void` - Subscribe to changes
|
|
350
|
+
|
|
351
|
+
### `useStorage<T>(item: StorageItem<T>)`
|
|
352
|
+
|
|
353
|
+
React hook using `useSyncExternalStore` for automatic re-renders.
|
|
354
|
+
|
|
355
|
+
**Returns:** `[value: T, setValue: (value: T) => void]`
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 🔐 Security
|
|
360
|
+
|
|
361
|
+
### iOS
|
|
362
|
+
|
|
363
|
+
- **Disk:** `UserDefaults.standard`
|
|
364
|
+
- **Secure:** Keychain with `kSecAttrAccessibleWhenUnlocked`
|
|
365
|
+
|
|
366
|
+
### Android
|
|
367
|
+
|
|
368
|
+
- **Disk:** `SharedPreferences`
|
|
369
|
+
- **Secure:** `EncryptedSharedPreferences` with AES256-GCM encryption
|
|
370
|
+
|
|
371
|
+
All secure operations use platform-native encryption. No data is stored in plain text.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 🧪 Testing
|
|
376
|
+
|
|
377
|
+
Comprehensive test coverage for both TypeScript and C++:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
# TypeScript/Jest tests
|
|
381
|
+
npm test
|
|
382
|
+
|
|
383
|
+
# Type checking
|
|
384
|
+
npm run typecheck
|
|
385
|
+
|
|
386
|
+
# Build verification
|
|
387
|
+
npm run build
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Test Coverage:**
|
|
391
|
+
|
|
392
|
+
- ✅ All storage scopes (Memory, Disk, Secure)
|
|
393
|
+
- ✅ Custom serialization
|
|
394
|
+
- ✅ Complex objects
|
|
395
|
+
- ✅ Subscription/unsubscription
|
|
396
|
+
- ✅ Memory leak prevention
|
|
397
|
+
- ✅ Thread safety (C++)
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 🏗️ Architecture
|
|
402
|
+
|
|
403
|
+
Built on [Nitro Modules](https://nitro.margelo.com) for maximum performance:
|
|
404
|
+
|
|
405
|
+
- **C++ Core:** Thread-safe storage implementation with mutex protection
|
|
406
|
+
- **JSI Bridge:** Zero-copy, synchronous JavaScript ↔ C++ communication
|
|
407
|
+
- **Platform Adapters:** Native iOS (Objective-C++) and Android (Kotlin + JNI) implementations
|
|
408
|
+
- **React Integration:** `useSyncExternalStore` for optimal React 18+ compatibility
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 🆚 Comparison
|
|
413
|
+
|
|
414
|
+
| Feature | Nitro Storage | MMKV | AsyncStorage | Zustand | Expo Secure Store |
|
|
415
|
+
| ---------------- | ------------- | ---- | ------------ | ------- | ----------------- |
|
|
416
|
+
| Synchronous | ✅ | ✅ | ❌ | ✅ | ❌ |
|
|
417
|
+
| Memory State | ✅ | ❌ | ❌ | ✅ | ❌ |
|
|
418
|
+
| Disk Persistence | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
419
|
+
| Secure Storage | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
420
|
+
| Type-Safe | ✅ | ⚠️ | ⚠️ | ✅ | ⚠️ |
|
|
421
|
+
| Unified API | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
422
|
+
| React Hooks | ✅ | ❌ | ❌ | ✅ | ❌ |
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 📄 License
|
|
427
|
+
|
|
428
|
+
MIT © [Your Name]
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 🙏 Acknowledgments
|
|
433
|
+
|
|
434
|
+
Built with ❤️ using [Nitro Modules](https://nitro.margelo.com) by [Marc Rousavy](https://github.com/mrousavy)
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 📚 Learn More
|
|
439
|
+
|
|
440
|
+
- [Nitro Modules Documentation](https://nitro.margelo.com)
|
|
441
|
+
- [Example App](./apps/example)
|
|
442
|
+
- [API Reference](#-api-reference)
|
|
443
|
+
- [Performance Benchmarks](#-performance-benchmarks)
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
**Keywords:** react-native storage, react-native state management, react-native keychain, react-native secure storage, react-native mmkv alternative, react-native async storage, synchronous storage react native, jsi storage, nitro modules, react native persistence
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22.1)
|
|
2
|
+
project(NitroStorage)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_C_STANDARD 11)
|
|
7
|
+
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
8
|
+
|
|
9
|
+
# Define the path to our C++ sources
|
|
10
|
+
set(CPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../cpp")
|
|
11
|
+
|
|
12
|
+
# Collect source files
|
|
13
|
+
file(GLOB CORE_SOURCES "${CPP_ROOT}/core/*.cpp")
|
|
14
|
+
file(GLOB BINDING_SOURCES "${CPP_ROOT}/bindings/*.cpp")
|
|
15
|
+
|
|
16
|
+
# Create the shared library with our sources
|
|
17
|
+
add_library(${PROJECT_NAME} SHARED
|
|
18
|
+
${CORE_SOURCES}
|
|
19
|
+
${BINDING_SOURCES}
|
|
20
|
+
# JNI adapter
|
|
21
|
+
src/main/cpp/cpp-adapter.cpp
|
|
22
|
+
src/main/cpp/AndroidStorageAdapterCpp.cpp
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Include directories
|
|
26
|
+
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
27
|
+
"${CPP_ROOT}/core"
|
|
28
|
+
"${CPP_ROOT}/bindings"
|
|
29
|
+
"src/main/cpp"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Include Nitro autolinking (adds nitrogen sources, definitions, and links)
|
|
33
|
+
include(${CMAKE_CURRENT_SOURCE_DIR}/../nitrogen/generated/android/NitroStorage+autolinking.cmake)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:8.10.1"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def reactNativeArchitectures() {
|
|
13
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
14
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def isNewArchitectureEnabled() {
|
|
18
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply plugin: "com.android.library"
|
|
22
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
23
|
+
// Apply autolinking script for Nitro
|
|
24
|
+
apply from: "../nitrogen/generated/android/NitroStorage+autolinking.gradle"
|
|
25
|
+
|
|
26
|
+
if (isNewArchitectureEnabled()) {
|
|
27
|
+
apply plugin: "com.facebook.react"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def getExtOrDefault(name) {
|
|
31
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroStorage_" + name]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def getExtOrIntegerDefault(name) {
|
|
35
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroStorage_" + name]).toInteger()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
android {
|
|
39
|
+
namespace "com.nitrostorage"
|
|
40
|
+
|
|
41
|
+
// Use values from root project or defaults
|
|
42
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
43
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
44
|
+
|
|
45
|
+
defaultConfig {
|
|
46
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
47
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
48
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
49
|
+
|
|
50
|
+
externalNativeBuild {
|
|
51
|
+
cmake {
|
|
52
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
53
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
54
|
+
abiFilters (*reactNativeArchitectures())
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
externalNativeBuild {
|
|
60
|
+
cmake {
|
|
61
|
+
path "CMakeLists.txt"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
buildFeatures {
|
|
66
|
+
buildConfig true
|
|
67
|
+
prefab true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
compileOptions {
|
|
71
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
72
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
sourceSets {
|
|
76
|
+
main {
|
|
77
|
+
java.srcDirs += ["src/main/java"]
|
|
78
|
+
if (isNewArchitectureEnabled()) {
|
|
79
|
+
java.srcDirs += ["${project.buildDir}/generated/source/codegen/java"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
repositories {
|
|
86
|
+
mavenCentral()
|
|
87
|
+
google()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
dependencies {
|
|
91
|
+
//noinspection GradleDynamicVersion
|
|
92
|
+
implementation "com.facebook.react:react-native:+"
|
|
93
|
+
implementation "androidx.security:security-crypto:1.1.0-alpha06"
|
|
94
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"
|
|
95
|
+
implementation project(":react-native-nitro-modules")
|
|
96
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#include "AndroidStorageAdapterCpp.hpp"
|
|
2
|
+
|
|
3
|
+
namespace NitroStorage {
|
|
4
|
+
|
|
5
|
+
using namespace facebook::jni;
|
|
6
|
+
|
|
7
|
+
AndroidStorageAdapterCpp::AndroidStorageAdapterCpp(alias_ref<JObject> context) {
|
|
8
|
+
if (!context) [[unlikely]] {
|
|
9
|
+
throw std::runtime_error("NitroStorage: Android Context is null");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
AndroidStorageAdapterCpp::~AndroidStorageAdapterCpp() = default;
|
|
14
|
+
|
|
15
|
+
void AndroidStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
|
|
16
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setDisk");
|
|
17
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::optional<std::string> AndroidStorageAdapterCpp::getDisk(const std::string& key) {
|
|
21
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jstring(std::string)>("getDisk");
|
|
22
|
+
auto result = method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
23
|
+
if (!result) return std::nullopt;
|
|
24
|
+
return result->toStdString();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
void AndroidStorageAdapterCpp::deleteDisk(const std::string& key) {
|
|
28
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string)>("deleteDisk");
|
|
29
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void AndroidStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
|
|
33
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecure");
|
|
34
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
std::optional<std::string> AndroidStorageAdapterCpp::getSecure(const std::string& key) {
|
|
38
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jstring(std::string)>("getSecure");
|
|
39
|
+
auto result = method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
40
|
+
if (!result) return std::nullopt;
|
|
41
|
+
return result->toStdString();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void AndroidStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
45
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string)>("deleteSecure");
|
|
46
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
} // namespace NitroStorage
|