react-native-nitro-storage 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -24
- package/android/CMakeLists.txt +30 -24
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +10 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +13 -0
- package/android/src/main/cpp/cpp-adapter.cpp +3 -2
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +10 -0
- package/cpp/bindings/HybridStorage.cpp +59 -17
- package/cpp/bindings/HybridStorage.hpp +4 -0
- package/cpp/core/NativeStorageAdapter.hpp +3 -0
- package/ios/IOSStorageAdapterCpp.hpp +3 -0
- package/ios/IOSStorageAdapterCpp.mm +41 -77
- package/lib/commonjs/Storage.nitro.js +0 -7
- package/lib/commonjs/Storage.nitro.js.map +1 -1
- package/lib/commonjs/Storage.types.js +13 -0
- package/lib/commonjs/Storage.types.js.map +1 -0
- package/lib/commonjs/index.js +127 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +268 -0
- package/lib/commonjs/index.web.js.map +1 -0
- package/lib/commonjs/migration.js +39 -0
- package/lib/commonjs/migration.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/Storage.nitro.js +1 -6
- package/lib/module/Storage.nitro.js.map +1 -1
- package/lib/module/Storage.types.js +9 -0
- package/lib/module/Storage.types.js.map +1 -0
- package/lib/module/index.js +122 -13
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +257 -0
- package/lib/module/index.web.js.map +1 -0
- package/lib/module/migration.js +35 -0
- package/lib/module/migration.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +5 -6
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +6 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +20 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +50 -0
- package/lib/typescript/index.web.d.ts.map +1 -0
- package/lib/typescript/migration.d.ts +12 -0
- package/lib/typescript/migration.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroStorage+autolinking.cmake +1 -1
- package/nitrogen/generated/android/NitroStorage+autolinking.gradle +1 -1
- package/nitrogen/generated/android/NitroStorageOnLoad.cpp +1 -1
- package/nitrogen/generated/android/NitroStorageOnLoad.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitrostorage/NitroStorageOnLoad.kt +1 -1
- package/nitrogen/generated/ios/NitroStorage+autolinking.rb +2 -2
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroStorageAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroStorageAutolinking.swift +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +5 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +6 -1
- package/package.json +7 -25
- package/src/Storage.nitro.ts +6 -7
- package/src/Storage.types.ts +5 -0
- package/src/index.ts +153 -16
- package/src/index.web.ts +341 -0
- package/src/migration.ts +53 -0
package/README.md
CHANGED
|
@@ -35,7 +35,11 @@ Familiar, elegant API with `createStorageItem` and `useStorage`. Works inside an
|
|
|
35
35
|
|
|
36
36
|
### **Production-Ready**
|
|
37
37
|
|
|
38
|
-
Thread-safe C++ core, comprehensive test coverage, and battle-tested on iOS and
|
|
38
|
+
Thread-safe C++ core, comprehensive test coverage, and battle-tested on iOS, Android, and **Web**.
|
|
39
|
+
|
|
40
|
+
### **Web Support**
|
|
41
|
+
|
|
42
|
+
Fully functional on the web via `localStorage` and `sessionStorage`. All hooks and storage atoms are fully reactive across all platforms.
|
|
39
43
|
|
|
40
44
|
---
|
|
41
45
|
|
|
@@ -139,17 +143,25 @@ const value = counterAtom.get(); // 42, instantly
|
|
|
139
143
|
|
|
140
144
|
_Replaces: Zustand, Jotai, Redux_
|
|
141
145
|
|
|
142
|
-
Fast, in-memory state.
|
|
146
|
+
Fast, in-memory state. **Now Pure JS** - can store complex objects, functions, and React nodes!
|
|
143
147
|
|
|
144
148
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
// Store a function
|
|
150
|
+
const callbackAtom = createStorageItem({
|
|
151
|
+
key: "on-click",
|
|
147
152
|
scope: StorageScope.Memory,
|
|
148
|
-
defaultValue:
|
|
153
|
+
defaultValue: () => console.log("Clicked!"),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Store a React Component
|
|
157
|
+
const modalAtom = createStorageItem({
|
|
158
|
+
key: "active-modal",
|
|
159
|
+
scope: StorageScope.Memory,
|
|
160
|
+
defaultValue: <View />,
|
|
149
161
|
});
|
|
150
162
|
```
|
|
151
163
|
|
|
152
|
-
**Performance:** < 0.001ms per operation
|
|
164
|
+
**Performance:** < 0.001ms per operation (Zero JSI overhead)
|
|
153
165
|
|
|
154
166
|
### **Disk Storage**
|
|
155
167
|
|
|
@@ -299,6 +311,78 @@ const unsubscribe = counterAtom.subscribe(() => {
|
|
|
299
311
|
unsubscribe();
|
|
300
312
|
```
|
|
301
313
|
|
|
314
|
+
### Batch Operations
|
|
315
|
+
|
|
316
|
+
Read or write multiple items at once. This is highly optimized on the native side for minimum overhead.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { getBatch, setBatch, removeBatch } from "react-native-nitro-storage";
|
|
320
|
+
|
|
321
|
+
// Write multiple items
|
|
322
|
+
setBatch(
|
|
323
|
+
[
|
|
324
|
+
{ item: item1, value: "v1" },
|
|
325
|
+
{ item: item2, value: "v2" },
|
|
326
|
+
],
|
|
327
|
+
StorageScope.Disk
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// Read multiple items
|
|
331
|
+
const [v1, v2] = getBatch([item1, item2], StorageScope.Disk);
|
|
332
|
+
|
|
333
|
+
// Remove multiple items
|
|
334
|
+
removeBatch([item1, item2], StorageScope.Disk);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Functional Updates
|
|
338
|
+
|
|
339
|
+
Update state based on the previous value, just like `useState`.
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Increment counter
|
|
343
|
+
counterAtom.set((prev) => prev + 1);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Optimized Writes
|
|
347
|
+
|
|
348
|
+
Use `useSetStorage` to set values without subscribing to updates (avoids re-renders).
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import { useSetStorage } from "react-native-nitro-storage";
|
|
352
|
+
|
|
353
|
+
function IncrementButton() {
|
|
354
|
+
const setCount = useSetStorage(counterAtom);
|
|
355
|
+
return <Button onPress={() => setCount((c) => c + 1)} title="+" />;
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Clearing Data
|
|
360
|
+
|
|
361
|
+
Clear entire storage scopes at once.
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { storage } from "react-native-nitro-storage";
|
|
365
|
+
|
|
366
|
+
// Clear all storage (all scopes)
|
|
367
|
+
storage.clearAll();
|
|
368
|
+
|
|
369
|
+
// Clear specific scope
|
|
370
|
+
storage.clear(StorageScope.Memory);
|
|
371
|
+
storage.clear(StorageScope.Disk);
|
|
372
|
+
storage.clear(StorageScope.Secure);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Migration from MMKV
|
|
376
|
+
|
|
377
|
+
Easily migrate data from `react-native-mmkv` to Nitro Storage.
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
import { migrateFromMMKV } from "react-native-nitro-storage/src/migration";
|
|
381
|
+
|
|
382
|
+
// Migrate 'user-settings' and delete from MMKV
|
|
383
|
+
migrateFromMMKV(mmkvInstance, settingsAtom, true);
|
|
384
|
+
```
|
|
385
|
+
|
|
302
386
|
---
|
|
303
387
|
|
|
304
388
|
## 📊 Performance Benchmarks
|
|
@@ -344,7 +428,7 @@ Creates a storage atom.
|
|
|
344
428
|
**Methods:**
|
|
345
429
|
|
|
346
430
|
- `get(): T` - Get current value (synchronous)
|
|
347
|
-
- `set(value: T): void` - Set new value (synchronous)
|
|
431
|
+
- `set(value: T | ((prev: T) => T)): void` - Set new value (synchronous)
|
|
348
432
|
- `delete(): void` - Remove value (synchronous)
|
|
349
433
|
- `subscribe(callback: () => void): () => void` - Subscribe to changes
|
|
350
434
|
|
|
@@ -352,7 +436,24 @@ Creates a storage atom.
|
|
|
352
436
|
|
|
353
437
|
React hook using `useSyncExternalStore` for automatic re-renders.
|
|
354
438
|
|
|
355
|
-
**Returns:** `[value: T, setValue: (value: T) => void]`
|
|
439
|
+
**Returns:** `[value: T, setValue: (value: T | ((prev: T) => T)) => void]`
|
|
440
|
+
|
|
441
|
+
### `useSetStorage<T>(item: StorageItem<T>)`
|
|
442
|
+
|
|
443
|
+
Returns the setter function only. Does not subscribe to updates.
|
|
444
|
+
|
|
445
|
+
**Returns:** `(value: T | ((prev: T) => T)) => void`
|
|
446
|
+
|
|
447
|
+
### `storage` Object
|
|
448
|
+
|
|
449
|
+
- `clearAll()`: Clears all storage across all scopes.
|
|
450
|
+
- `clear(scope)`: Clears a specific storage scope.
|
|
451
|
+
|
|
452
|
+
### Batch Operations
|
|
453
|
+
|
|
454
|
+
- `getBatch(items, scope)`: Returns an array of values for the given items.
|
|
455
|
+
- `setBatch(items, scope)`: Sets multiple values at once.
|
|
456
|
+
- `removeBatch(items, scope)`: Removes multiple items at once.
|
|
356
457
|
|
|
357
458
|
---
|
|
358
459
|
|
|
@@ -420,27 +521,13 @@ Built on [Nitro Modules](https://nitro.margelo.com) for maximum performance:
|
|
|
420
521
|
| Type-Safe | ✅ | ⚠️ | ⚠️ | ✅ | ⚠️ |
|
|
421
522
|
| Unified API | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
422
523
|
| React Hooks | ✅ | ❌ | ❌ | ✅ | ❌ |
|
|
524
|
+
| Web Support | ✅ | ❌ | ✅ | ✅ | ❌ |
|
|
423
525
|
|
|
424
526
|
---
|
|
425
527
|
|
|
426
528
|
## 📄 License
|
|
427
529
|
|
|
428
|
-
MIT
|
|
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)
|
|
530
|
+
MIT
|
|
444
531
|
|
|
445
532
|
---
|
|
446
533
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.
|
|
1
|
+
cmake_minimum_required(VERSION 3.18.0)
|
|
2
|
+
|
|
2
3
|
project(NitroStorage)
|
|
3
4
|
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
4
6
|
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
7
|
|
|
12
|
-
#
|
|
13
|
-
file(GLOB
|
|
14
|
-
|
|
8
|
+
# 1. Define source files (Manual implementation)
|
|
9
|
+
file(GLOB SOURCES
|
|
10
|
+
"../cpp/bindings/*.cpp"
|
|
11
|
+
"../cpp/core/*.cpp"
|
|
12
|
+
"./src/main/cpp/*.cpp"
|
|
13
|
+
)
|
|
15
14
|
|
|
16
|
-
# Create the
|
|
17
|
-
add_library(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
src/main/cpp/cpp-adapter.cpp
|
|
22
|
-
src/main/cpp/AndroidStorageAdapterCpp.cpp
|
|
15
|
+
# 2. Create the library target
|
|
16
|
+
add_library(
|
|
17
|
+
NitroStorage
|
|
18
|
+
SHARED
|
|
19
|
+
${SOURCES}
|
|
23
20
|
)
|
|
24
21
|
|
|
25
|
-
# Include directories
|
|
26
|
-
target_include_directories(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
# 3. Include directories for manual sources
|
|
23
|
+
target_include_directories(
|
|
24
|
+
NitroStorage
|
|
25
|
+
PRIVATE
|
|
26
|
+
"../cpp/core"
|
|
27
|
+
"../cpp/bindings"
|
|
28
|
+
"./src/main/cpp"
|
|
30
29
|
)
|
|
31
30
|
|
|
32
|
-
# Include
|
|
33
|
-
include(
|
|
31
|
+
# 4. Include Nitrogen Autolinking (adds generated sources, finding packages, linking libs)
|
|
32
|
+
include("../nitrogen/generated/android/NitroStorage+autolinking.cmake")
|
|
33
|
+
|
|
34
|
+
# 5. Link standard Android libraries
|
|
35
|
+
target_link_libraries(
|
|
36
|
+
NitroStorage
|
|
37
|
+
android
|
|
38
|
+
log
|
|
39
|
+
)
|
|
@@ -46,4 +46,14 @@ void AndroidStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
|
46
46
|
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
void AndroidStorageAdapterCpp::clearDisk() {
|
|
50
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearDisk");
|
|
51
|
+
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void AndroidStorageAdapterCpp::clearSecure() {
|
|
55
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecure");
|
|
56
|
+
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
} // namespace NitroStorage
|
|
@@ -49,6 +49,16 @@ struct AndroidStorageAdapterJava : facebook::jni::JavaClass<AndroidStorageAdapte
|
|
|
49
49
|
static auto method = javaClassStatic()->getMethod<void(std::string)>("deleteSecure");
|
|
50
50
|
method(self(), key);
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
void clearDisk() {
|
|
54
|
+
static auto method = javaClassStatic()->getStaticMethod<void()>("clearDisk");
|
|
55
|
+
method(javaClassStatic());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void clearSecure() {
|
|
59
|
+
static auto method = javaClassStatic()->getStaticMethod<void()>("clearSecure");
|
|
60
|
+
method(javaClassStatic());
|
|
61
|
+
}
|
|
52
62
|
};
|
|
53
63
|
|
|
54
64
|
class AndroidStorageAdapterCpp : public NativeStorageAdapter {
|
|
@@ -63,6 +73,9 @@ public:
|
|
|
63
73
|
void setSecure(const std::string& key, const std::string& value) override;
|
|
64
74
|
std::optional<std::string> getSecure(const std::string& key) override;
|
|
65
75
|
void deleteSecure(const std::string& key) override;
|
|
76
|
+
|
|
77
|
+
void clearDisk() override;
|
|
78
|
+
void clearSecure() override;
|
|
66
79
|
};
|
|
67
80
|
|
|
68
81
|
} // namespace NitroStorage
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include <jni.h>
|
|
2
|
-
#include
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
3
|
+
#include "../../../nitrogen/generated/android/NitroStorageOnLoad.hpp"
|
|
3
4
|
|
|
4
|
-
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*
|
|
5
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
5
6
|
return margelo::nitro::NitroStorage::initialize(vm);
|
|
6
7
|
}
|
|
@@ -90,5 +90,15 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
90
90
|
fun deleteSecure(key: String) {
|
|
91
91
|
instance?.encryptedPreferences?.edit()?.remove(key)?.apply()
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
@JvmStatic
|
|
95
|
+
fun clearDisk() {
|
|
96
|
+
instance?.sharedPreferences?.edit()?.clear()?.apply()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@JvmStatic
|
|
100
|
+
fun clearSecure() {
|
|
101
|
+
instance?.encryptedPreferences?.edit()?.clear()?.apply()
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -24,14 +24,7 @@ HybridStorage::HybridStorage(std::shared_ptr<::NitroStorage::NativeStorageAdapte
|
|
|
24
24
|
: HybridObject(TAG), HybridStorageSpec(), nativeAdapter_(std::move(adapter)) {}
|
|
25
25
|
|
|
26
26
|
HybridStorage::Scope HybridStorage::toScope(double scopeValue) {
|
|
27
|
-
|
|
28
|
-
if (intScope < 0 || intScope > 2) {
|
|
29
|
-
throw std::invalid_argument(
|
|
30
|
-
"Invalid storage scope: " + std::to_string(intScope) +
|
|
31
|
-
". Must be 0 (Memory), 1 (Disk), or 2 (Secure)"
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
return static_cast<Scope>(intScope);
|
|
27
|
+
return static_cast<Scope>(static_cast<int>(scopeValue));
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
void HybridStorage::set(const std::string& key, const std::string& value, double scope) {
|
|
@@ -100,8 +93,8 @@ std::function<void()> HybridStorage::addOnChange(
|
|
|
100
93
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
101
94
|
) {
|
|
102
95
|
int intScope = static_cast<int>(scope);
|
|
103
|
-
|
|
104
96
|
size_t listenerId;
|
|
97
|
+
|
|
105
98
|
{
|
|
106
99
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
107
100
|
listenerId = nextListenerId_++;
|
|
@@ -111,17 +104,65 @@ std::function<void()> HybridStorage::addOnChange(
|
|
|
111
104
|
return [this, intScope, listenerId]() {
|
|
112
105
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
113
106
|
auto& scopeListeners = listeners_[intScope];
|
|
114
|
-
scopeListeners.
|
|
115
|
-
|
|
116
|
-
scopeListeners.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
scopeListeners.end()
|
|
121
|
-
);
|
|
107
|
+
for (auto it = scopeListeners.begin(); it != scopeListeners.end(); ++it) {
|
|
108
|
+
if (it->id == listenerId) {
|
|
109
|
+
scopeListeners.erase(it);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
122
113
|
};
|
|
123
114
|
}
|
|
124
115
|
|
|
116
|
+
void HybridStorage::clear(double scope) {
|
|
117
|
+
Scope s = toScope(scope);
|
|
118
|
+
|
|
119
|
+
switch (s) {
|
|
120
|
+
case Scope::Memory: {
|
|
121
|
+
std::lock_guard<std::mutex> lock(memoryMutex_);
|
|
122
|
+
memoryStore_.clear();
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case Scope::Disk:
|
|
126
|
+
nativeAdapter_->clearDisk();
|
|
127
|
+
break;
|
|
128
|
+
case Scope::Secure:
|
|
129
|
+
nativeAdapter_->clearSecure();
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
notifyListeners(static_cast<int>(s), "", std::nullopt);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void HybridStorage::setBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values, double scope) {
|
|
137
|
+
if (keys.size() != values.size()) {
|
|
138
|
+
throw std::runtime_error("NitroStorage: Keys and values size mismatch in setBatch");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (size_t i = 0; i < keys.size(); ++i) {
|
|
142
|
+
set(keys[i], values[i], scope);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>& keys, double scope) {
|
|
148
|
+
std::vector<std::string> results;
|
|
149
|
+
results.reserve(keys.size());
|
|
150
|
+
|
|
151
|
+
for (const auto& key : keys) {
|
|
152
|
+
auto val = get(key, scope);
|
|
153
|
+
results.push_back(val.value_or(""));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
void HybridStorage::removeBatch(const std::vector<std::string>& keys, double scope) {
|
|
161
|
+
for (const auto& key : keys) {
|
|
162
|
+
remove(key, scope);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
125
166
|
void HybridStorage::notifyListeners(
|
|
126
167
|
int scope,
|
|
127
168
|
const std::string& key,
|
|
@@ -132,6 +173,7 @@ void HybridStorage::notifyListeners(
|
|
|
132
173
|
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
133
174
|
auto it = listeners_.find(scope);
|
|
134
175
|
if (it != listeners_.end()) {
|
|
176
|
+
listenersCopy.reserve(it->second.size());
|
|
135
177
|
listenersCopy = it->second;
|
|
136
178
|
}
|
|
137
179
|
}
|
|
@@ -19,6 +19,10 @@ public:
|
|
|
19
19
|
void set(const std::string& key, const std::string& value, double scope) override;
|
|
20
20
|
std::optional<std::string> get(const std::string& key, double scope) override;
|
|
21
21
|
void remove(const std::string& key, double scope) override;
|
|
22
|
+
void clear(double scope) override;
|
|
23
|
+
void setBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values, double scope) override;
|
|
24
|
+
std::vector<std::string> getBatch(const std::vector<std::string>& keys, double scope) override;
|
|
25
|
+
void removeBatch(const std::vector<std::string>& keys, double scope) override;
|
|
22
26
|
std::function<void()> addOnChange(
|
|
23
27
|
double scope,
|
|
24
28
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
@@ -16,6 +16,9 @@ public:
|
|
|
16
16
|
virtual void setSecure(const std::string& key, const std::string& value) = 0;
|
|
17
17
|
virtual std::optional<std::string> getSecure(const std::string& key) = 0;
|
|
18
18
|
virtual void deleteSecure(const std::string& key) = 0;
|
|
19
|
+
|
|
20
|
+
virtual void clearDisk() = 0;
|
|
21
|
+
virtual void clearSecure() = 0;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
} // namespace NitroStorage
|
|
@@ -16,6 +16,9 @@ public:
|
|
|
16
16
|
void setSecure(const std::string& key, const std::string& value) override;
|
|
17
17
|
std::optional<std::string> getSecure(const std::string& key) override;
|
|
18
18
|
void deleteSecure(const std::string& key) override;
|
|
19
|
+
|
|
20
|
+
void clearDisk() override;
|
|
21
|
+
void clearSecure() override;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
} // namespace NitroStorage
|
|
@@ -4,124 +4,88 @@
|
|
|
4
4
|
|
|
5
5
|
namespace NitroStorage {
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
static NSString* const kKeychainService = @"com.nitrostorage.keychain";
|
|
8
8
|
|
|
9
|
+
IOSStorageAdapterCpp::IOSStorageAdapterCpp() {}
|
|
9
10
|
IOSStorageAdapterCpp::~IOSStorageAdapterCpp() {}
|
|
10
11
|
|
|
11
12
|
void IOSStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[[NSUserDefaults standardUserDefaults] setObject:nsValue forKey:nsKey];
|
|
13
|
+
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:value.c_str()]
|
|
14
|
+
forKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
std::optional<std::string> IOSStorageAdapterCpp::getDisk(const std::string& key) {
|
|
18
|
-
NSString*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (result) {
|
|
22
|
-
const char* utf8String = [result UTF8String];
|
|
23
|
-
if (utf8String) {
|
|
24
|
-
std::string value;
|
|
25
|
-
value.reserve([result lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
26
|
-
value.assign(utf8String);
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return std::nullopt;
|
|
18
|
+
NSString* result = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
19
|
+
if (!result) return std::nullopt;
|
|
20
|
+
return std::string([result UTF8String]);
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
void IOSStorageAdapterCpp::deleteDisk(const std::string& key) {
|
|
34
|
-
|
|
35
|
-
[[NSUserDefaults standardUserDefaults] removeObjectForKey:nsKey];
|
|
24
|
+
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithUTF8String:key.c_str()]];
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
|
|
39
28
|
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
44
|
-
|
|
29
|
+
NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
|
|
30
|
+
|
|
45
31
|
NSDictionary* query = @{
|
|
46
32
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
47
|
-
(__bridge id)kSecAttrService:
|
|
33
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
48
34
|
(__bridge id)kSecAttrAccount: nsKey
|
|
49
35
|
};
|
|
50
|
-
|
|
36
|
+
|
|
51
37
|
NSDictionary* updateAttributes = @{
|
|
52
38
|
(__bridge id)kSecValueData: data
|
|
53
39
|
};
|
|
40
|
+
|
|
41
|
+
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
NSDictionary* addQuery = @{
|
|
61
|
-
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
62
|
-
(__bridge id)kSecAttrService: service,
|
|
63
|
-
(__bridge id)kSecAttrAccount: nsKey,
|
|
64
|
-
(__bridge id)kSecValueData: data,
|
|
65
|
-
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
|
|
69
|
-
if (addStatus != errSecSuccess) {
|
|
70
|
-
NSLog(@"NitroStorage: Failed to add to Keychain for key '%@'. Error: %d", nsKey, (int)addStatus);
|
|
71
|
-
}
|
|
72
|
-
} else if (updateStatus != errSecSuccess) {
|
|
73
|
-
NSLog(@"NitroStorage: Failed to update Keychain item '%@'. Error: %d", nsKey, (int)updateStatus);
|
|
43
|
+
if (status == errSecItemNotFound) {
|
|
44
|
+
NSMutableDictionary* addQuery = [query mutableCopy];
|
|
45
|
+
addQuery[(__bridge id)kSecValueData] = data;
|
|
46
|
+
addQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked;
|
|
47
|
+
SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
|
|
74
48
|
}
|
|
75
49
|
}
|
|
76
50
|
|
|
77
51
|
std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
|
|
78
|
-
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
79
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
80
|
-
|
|
81
52
|
NSDictionary* query = @{
|
|
82
53
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
83
|
-
(__bridge id)kSecAttrService:
|
|
84
|
-
(__bridge id)kSecAttrAccount:
|
|
54
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
55
|
+
(__bridge id)kSecAttrAccount: [NSString stringWithUTF8String:key.c_str()],
|
|
85
56
|
(__bridge id)kSecReturnData: @YES,
|
|
86
57
|
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
|
|
87
58
|
};
|
|
88
|
-
|
|
59
|
+
|
|
89
60
|
CFTypeRef result = NULL;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (status == errSecSuccess && result) {
|
|
61
|
+
if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == errSecSuccess && result) {
|
|
93
62
|
NSData* data = (__bridge_transfer NSData*)result;
|
|
94
|
-
NSString*
|
|
95
|
-
if (
|
|
96
|
-
const char* utf8String = [nsValue UTF8String];
|
|
97
|
-
if (utf8String) {
|
|
98
|
-
std::string value;
|
|
99
|
-
value.reserve([nsValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
100
|
-
value.assign(utf8String);
|
|
101
|
-
return value;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
} else if (status != errSecItemNotFound) {
|
|
105
|
-
NSLog(@"NitroStorage: Failed to read from Keychain for key '%@'. Error: %d", nsKey, (int)status);
|
|
63
|
+
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
64
|
+
if (str) return std::string([str UTF8String]);
|
|
106
65
|
}
|
|
107
|
-
|
|
108
66
|
return std::nullopt;
|
|
109
67
|
}
|
|
110
68
|
|
|
111
69
|
void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
112
|
-
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
113
|
-
NSString* service = @"com.nitrostorage.keychain";
|
|
114
|
-
|
|
115
70
|
NSDictionary* query = @{
|
|
116
71
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
117
|
-
(__bridge id)kSecAttrService:
|
|
118
|
-
(__bridge id)kSecAttrAccount:
|
|
72
|
+
(__bridge id)kSecAttrService: kKeychainService,
|
|
73
|
+
(__bridge id)kSecAttrAccount: [NSString stringWithUTF8String:key.c_str()]
|
|
119
74
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
75
|
+
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void IOSStorageAdapterCpp::clearDisk() {
|
|
79
|
+
NSString* appDomain = [[NSBundle mainBundle] bundleIdentifier];
|
|
80
|
+
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void IOSStorageAdapterCpp::clearSecure() {
|
|
84
|
+
NSDictionary* query = @{
|
|
85
|
+
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
86
|
+
(__bridge id)kSecAttrService: kKeychainService
|
|
87
|
+
};
|
|
88
|
+
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
125
89
|
}
|
|
126
90
|
|
|
127
91
|
} // namespace NitroStorage
|
|
@@ -3,11 +3,4 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.StorageScope = void 0;
|
|
7
|
-
let StorageScope = exports.StorageScope = /*#__PURE__*/function (StorageScope) {
|
|
8
|
-
StorageScope[StorageScope["Memory"] = 0] = "Memory";
|
|
9
|
-
StorageScope[StorageScope["Disk"] = 1] = "Disk";
|
|
10
|
-
StorageScope[StorageScope["Secure"] = 2] = "Secure";
|
|
11
|
-
return StorageScope;
|
|
12
|
-
}({});
|
|
13
6
|
//# sourceMappingURL=Storage.nitro.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["Storage.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.StorageScope = void 0;
|
|
7
|
+
let StorageScope = exports.StorageScope = /*#__PURE__*/function (StorageScope) {
|
|
8
|
+
StorageScope[StorageScope["Memory"] = 0] = "Memory";
|
|
9
|
+
StorageScope[StorageScope["Disk"] = 1] = "Disk";
|
|
10
|
+
StorageScope[StorageScope["Secure"] = 2] = "Secure";
|
|
11
|
+
return StorageScope;
|
|
12
|
+
}({});
|
|
13
|
+
//# sourceMappingURL=Storage.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["StorageScope","exports"],"sourceRoot":"../../src","sources":["Storage.types.ts"],"mappings":";;;;;;IAAYA,YAAY,GAAAC,OAAA,CAAAD,YAAA,0BAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA","ignoreList":[]}
|