@fto-consult/expo-ui 6.37.3 → 6.37.5
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/App.js +0 -1
- package/node_modules/.package-lock.json +26218 -26188
- package/node_modules/@fto-consult/common/babel.config.alias.js +18 -1
- package/node_modules/@fto-consult/common/package.json +1 -1
- package/node_modules/@react-native-async-storage/async-storage/LICENSE +21 -0
- package/node_modules/@react-native-async-storage/async-storage/README.md +27 -0
- package/node_modules/@react-native-async-storage/async-storage/RNCAsyncStorage.podspec +19 -0
- package/node_modules/@react-native-async-storage/async-storage/android/build.gradle +142 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/AndroidManifest.xml +6 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +178 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageErrorUtil.java +45 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageExpoMigration.java +154 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +424 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +58 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/ReactDatabaseSupplier.java +163 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/SerialExecutor.java +40 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpers.kt +86 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ErrorHelpers.kt +39 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +90 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +161 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpersTest.kt +93 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/StorageTest.kt +141 -0
- package/node_modules/@react-native-async-storage/async-storage/android/testresults.gradle +38 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.h +51 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.m +898 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.xcodeproj/project.pbxproj +283 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorageDelegate.h +73 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.d.ts +9 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.js +109 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js +164 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js +366 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js +30 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js +69 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js +44 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js +22 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js +39 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js +153 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js +348 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js +20 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js +56 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js +34 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js +31 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.d.ts +10 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.native.d.ts +16 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/RCTAsyncStorage.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/helpers.d.ts +5 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/hooks.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/index.d.ts +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/shouldFallbackToLegacyNativeModule.d.ts +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/types.d.ts +113 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/project.pbxproj +385 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage-macOS.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/package.json +197 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.native.ts +356 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts +173 -0
- package/node_modules/@react-native-async-storage/async-storage/src/RCTAsyncStorage.ts +28 -0
- package/node_modules/@react-native-async-storage/async-storage/src/helpers.ts +74 -0
- package/node_modules/@react-native-async-storage/async-storage/src/hooks.ts +11 -0
- package/node_modules/@react-native-async-storage/async-storage/src/index.ts +7 -0
- package/node_modules/@react-native-async-storage/async-storage/src/shouldFallbackToLegacyNativeModule.ts +34 -0
- package/node_modules/@react-native-async-storage/async-storage/src/types.ts +155 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage.sln +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj +157 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61.sln +195 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage62.sln +192 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.cpp +599 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.h +162 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/RNCAsyncStorage.h +118 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactNativeAsyncStorage.def +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.cpp +20 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.h +23 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.idl +7 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.cpp +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.h +15 -0
- package/node_modules/merge-options/index.d.ts +2 -0
- package/node_modules/merge-options/index.js +171 -0
- package/node_modules/merge-options/index.mjs +8 -0
- package/node_modules/merge-options/license +21 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.d.ts +29 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.js +10 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/license +9 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/package.json +38 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/readme.md +54 -0
- package/node_modules/merge-options/package.json +59 -0
- package/node_modules/merge-options/readme.md +130 -0
- package/package.json +131 -130
- package/src/components/Chart/appexChart/appexChart.html +23 -23
@@ -0,0 +1,898 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#import "RNCAsyncStorage.h"
|
9
|
+
|
10
|
+
#import <CommonCrypto/CommonCryptor.h>
|
11
|
+
#import <CommonCrypto/CommonDigest.h>
|
12
|
+
|
13
|
+
#import <React/RCTConvert.h>
|
14
|
+
#import <React/RCTLog.h>
|
15
|
+
#import <React/RCTUtils.h>
|
16
|
+
|
17
|
+
static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1";
|
18
|
+
static NSString *const RCTOldStorageDirectory = @"RNCAsyncLocalStorage_V1";
|
19
|
+
static NSString *const RCTExpoStorageDirectory = @"RCTAsyncLocalStorage";
|
20
|
+
static NSString *const RCTManifestFileName = @"manifest.json";
|
21
|
+
static const NSUInteger RCTInlineValueThreshold = 1024;
|
22
|
+
|
23
|
+
#pragma mark - Static helper functions
|
24
|
+
|
25
|
+
static NSDictionary *RCTErrorForKey(NSString *key)
|
26
|
+
{
|
27
|
+
if (![key isKindOfClass:[NSString class]]) {
|
28
|
+
return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key});
|
29
|
+
} else if (key.length < 1) {
|
30
|
+
return RCTMakeAndLogError(
|
31
|
+
@"Invalid key - must be at least one character. Key: ", key, @{@"key": key});
|
32
|
+
} else {
|
33
|
+
return nil;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString *path, NSNumber *isExcluded)
|
38
|
+
{
|
39
|
+
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
40
|
+
|
41
|
+
BOOL isDir;
|
42
|
+
BOOL exists = [fileManager fileExistsAtPath:path isDirectory:&isDir];
|
43
|
+
BOOL success = false;
|
44
|
+
|
45
|
+
if (isDir && exists) {
|
46
|
+
NSURL *pathUrl = [NSURL fileURLWithPath:path];
|
47
|
+
NSError *error = nil;
|
48
|
+
success = [pathUrl setResourceValue:isExcluded
|
49
|
+
forKey:NSURLIsExcludedFromBackupKey
|
50
|
+
error:&error];
|
51
|
+
|
52
|
+
if (!success) {
|
53
|
+
NSLog(@"Could not exclude AsyncStorage dir from backup %@", error);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return success;
|
57
|
+
}
|
58
|
+
|
59
|
+
static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> **errors)
|
60
|
+
{
|
61
|
+
if (error && errors) {
|
62
|
+
if (!*errors) {
|
63
|
+
*errors = [NSMutableArray new];
|
64
|
+
}
|
65
|
+
[*errors addObject:error];
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
static NSArray<NSDictionary *> *RCTMakeErrors(NSArray<id<NSObject>> *results)
|
70
|
+
{
|
71
|
+
NSMutableArray<NSDictionary *> *errors;
|
72
|
+
for (id object in results) {
|
73
|
+
if ([object isKindOfClass:[NSError class]]) {
|
74
|
+
NSError *error = (NSError *)object;
|
75
|
+
NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil);
|
76
|
+
RCTAppendError(keyError, &errors);
|
77
|
+
}
|
78
|
+
}
|
79
|
+
return errors;
|
80
|
+
}
|
81
|
+
|
82
|
+
static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut)
|
83
|
+
{
|
84
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
85
|
+
NSError *error;
|
86
|
+
NSStringEncoding encoding;
|
87
|
+
NSString *entryString = [NSString stringWithContentsOfFile:filePath
|
88
|
+
usedEncoding:&encoding
|
89
|
+
error:&error];
|
90
|
+
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
91
|
+
|
92
|
+
if (error) {
|
93
|
+
if (errorOut) {
|
94
|
+
*errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData);
|
95
|
+
}
|
96
|
+
return nil;
|
97
|
+
}
|
98
|
+
|
99
|
+
if (encoding != NSUTF8StringEncoding) {
|
100
|
+
if (errorOut) {
|
101
|
+
*errorOut =
|
102
|
+
RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData);
|
103
|
+
}
|
104
|
+
return nil;
|
105
|
+
}
|
106
|
+
return entryString;
|
107
|
+
}
|
108
|
+
|
109
|
+
return nil;
|
110
|
+
}
|
111
|
+
|
112
|
+
// DO NOT USE
|
113
|
+
// This is used internally to migrate data from the old file location to the new one.
|
114
|
+
// Please use `RCTCreateStorageDirectoryPath` instead
|
115
|
+
static NSString *RCTCreateStorageDirectoryPath_deprecated(NSString *storageDir)
|
116
|
+
{
|
117
|
+
NSString *storageDirectoryPath;
|
118
|
+
#if TARGET_OS_TV
|
119
|
+
storageDirectoryPath =
|
120
|
+
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
121
|
+
#else
|
122
|
+
storageDirectoryPath =
|
123
|
+
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
124
|
+
#endif
|
125
|
+
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
126
|
+
return storageDirectoryPath;
|
127
|
+
}
|
128
|
+
|
129
|
+
static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir)
|
130
|
+
{
|
131
|
+
NSString *storageDirectoryPath = @"";
|
132
|
+
|
133
|
+
#if TARGET_OS_TV
|
134
|
+
storageDirectoryPath =
|
135
|
+
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
136
|
+
#else
|
137
|
+
storageDirectoryPath =
|
138
|
+
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
|
139
|
+
.firstObject;
|
140
|
+
// We should use the "Application Support/[bundleID]" folder for persistent data storage that's
|
141
|
+
// hidden from users
|
142
|
+
storageDirectoryPath = [storageDirectoryPath
|
143
|
+
stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
|
144
|
+
#endif
|
145
|
+
|
146
|
+
// Per Apple's docs, all app content in Application Support must be within a subdirectory of the
|
147
|
+
// app's bundle identifier
|
148
|
+
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
149
|
+
|
150
|
+
return storageDirectoryPath;
|
151
|
+
}
|
152
|
+
|
153
|
+
static NSString *RCTGetStorageDirectory()
|
154
|
+
{
|
155
|
+
static NSString *storageDirectory = nil;
|
156
|
+
static dispatch_once_t onceToken;
|
157
|
+
dispatch_once(&onceToken, ^{
|
158
|
+
storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory);
|
159
|
+
});
|
160
|
+
return storageDirectory;
|
161
|
+
}
|
162
|
+
|
163
|
+
static NSString *RCTCreateManifestFilePath(NSString *storageDirectory)
|
164
|
+
{
|
165
|
+
return [storageDirectory stringByAppendingPathComponent:RCTManifestFileName];
|
166
|
+
}
|
167
|
+
|
168
|
+
static NSString *RCTGetManifestFilePath()
|
169
|
+
{
|
170
|
+
static NSString *manifestFilePath = nil;
|
171
|
+
static dispatch_once_t onceToken;
|
172
|
+
dispatch_once(&onceToken, ^{
|
173
|
+
manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory);
|
174
|
+
});
|
175
|
+
return manifestFilePath;
|
176
|
+
}
|
177
|
+
|
178
|
+
// Only merges objects - all other types are just clobbered (including arrays)
|
179
|
+
static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
|
180
|
+
{
|
181
|
+
BOOL modified = NO;
|
182
|
+
for (NSString *key in source) {
|
183
|
+
id sourceValue = source[key];
|
184
|
+
id destinationValue = destination[key];
|
185
|
+
if ([sourceValue isKindOfClass:[NSDictionary class]]) {
|
186
|
+
if ([destinationValue isKindOfClass:[NSDictionary class]]) {
|
187
|
+
if ([destinationValue classForCoder] != [NSMutableDictionary class]) {
|
188
|
+
destinationValue = [destinationValue mutableCopy];
|
189
|
+
}
|
190
|
+
if (RCTMergeRecursive(destinationValue, sourceValue)) {
|
191
|
+
destination[key] = destinationValue;
|
192
|
+
modified = YES;
|
193
|
+
}
|
194
|
+
} else {
|
195
|
+
destination[key] = [sourceValue copy];
|
196
|
+
modified = YES;
|
197
|
+
}
|
198
|
+
} else if (![source isEqual:destinationValue]) {
|
199
|
+
destination[key] = [sourceValue copy];
|
200
|
+
modified = YES;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
return modified;
|
204
|
+
}
|
205
|
+
|
206
|
+
static dispatch_queue_t RCTGetMethodQueue()
|
207
|
+
{
|
208
|
+
// We want all instances to share the same queue since they will be reading/writing the same
|
209
|
+
// files.
|
210
|
+
static dispatch_queue_t queue;
|
211
|
+
static dispatch_once_t onceToken;
|
212
|
+
dispatch_once(&onceToken, ^{
|
213
|
+
queue =
|
214
|
+
dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
|
215
|
+
});
|
216
|
+
return queue;
|
217
|
+
}
|
218
|
+
|
219
|
+
static NSCache *RCTGetCache()
|
220
|
+
{
|
221
|
+
// We want all instances to share the same cache since they will be reading/writing the same
|
222
|
+
// files.
|
223
|
+
static NSCache *cache;
|
224
|
+
static dispatch_once_t onceToken;
|
225
|
+
dispatch_once(&onceToken, ^{
|
226
|
+
cache = [NSCache new];
|
227
|
+
cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
|
228
|
+
|
229
|
+
#if !TARGET_OS_OSX
|
230
|
+
// Clear cache in the event of a memory warning
|
231
|
+
[[NSNotificationCenter defaultCenter]
|
232
|
+
addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
|
233
|
+
object:nil
|
234
|
+
queue:nil
|
235
|
+
usingBlock:^(__unused NSNotification *note) {
|
236
|
+
[cache removeAllObjects];
|
237
|
+
}];
|
238
|
+
#endif // !TARGET_OS_OSX
|
239
|
+
});
|
240
|
+
return cache;
|
241
|
+
}
|
242
|
+
|
243
|
+
static BOOL RCTHasCreatedStorageDirectory = NO;
|
244
|
+
static NSDictionary *RCTDeleteStorageDirectory()
|
245
|
+
{
|
246
|
+
NSError *error;
|
247
|
+
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error];
|
248
|
+
RCTHasCreatedStorageDirectory = NO;
|
249
|
+
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil;
|
250
|
+
}
|
251
|
+
|
252
|
+
static NSDate *RCTManifestModificationDate(NSString *manifestFilePath)
|
253
|
+
{
|
254
|
+
NSDictionary *attributes =
|
255
|
+
[[NSFileManager defaultManager] attributesOfItemAtPath:manifestFilePath error:nil];
|
256
|
+
return [attributes fileModificationDate];
|
257
|
+
}
|
258
|
+
|
259
|
+
/**
|
260
|
+
* Creates an NSException used during Storage Directory Migration.
|
261
|
+
*/
|
262
|
+
static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *error)
|
263
|
+
{
|
264
|
+
RCTLogWarn(@"%@: %@", reason, error ? error.description : @"");
|
265
|
+
}
|
266
|
+
|
267
|
+
static void RCTStorageDirectoryCleanupOld(NSString *oldDirectoryPath)
|
268
|
+
{
|
269
|
+
NSError *error;
|
270
|
+
if (![[NSFileManager defaultManager] removeItemAtPath:oldDirectoryPath error:&error]) {
|
271
|
+
RCTStorageDirectoryMigrationLogError(
|
272
|
+
@"Failed to remove old storage directory during migration", error);
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
static void _createStorageDirectory(NSString *storageDirectory, NSError **error)
|
277
|
+
{
|
278
|
+
[[NSFileManager defaultManager] createDirectoryAtPath:storageDirectory
|
279
|
+
withIntermediateDirectories:YES
|
280
|
+
attributes:nil
|
281
|
+
error:error];
|
282
|
+
}
|
283
|
+
|
284
|
+
static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
|
285
|
+
NSString *newDirectoryPath,
|
286
|
+
BOOL shouldCleanupOldDirectory)
|
287
|
+
{
|
288
|
+
NSError *error;
|
289
|
+
// Migrate data by copying old storage directory to new storage directory location
|
290
|
+
if (![[NSFileManager defaultManager] copyItemAtPath:oldDirectoryPath
|
291
|
+
toPath:newDirectoryPath
|
292
|
+
error:&error]) {
|
293
|
+
// the new storage directory "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" seems
|
294
|
+
// unable to migrate because folder "Application Support/[bundleID]" doesn't exist.. create
|
295
|
+
// this folder and attempt folder copying again
|
296
|
+
if (error != nil && error.code == 4 &&
|
297
|
+
[newDirectoryPath isEqualToString:RCTGetStorageDirectory()]) {
|
298
|
+
NSError *error = nil;
|
299
|
+
_createStorageDirectory(RCTCreateStorageDirectoryPath(@""), &error);
|
300
|
+
if (error == nil) {
|
301
|
+
RCTStorageDirectoryMigrate(
|
302
|
+
oldDirectoryPath, newDirectoryPath, shouldCleanupOldDirectory);
|
303
|
+
} else {
|
304
|
+
RCTStorageDirectoryMigrationLogError(
|
305
|
+
@"Failed to create storage directory during migration.", error);
|
306
|
+
}
|
307
|
+
} else {
|
308
|
+
RCTStorageDirectoryMigrationLogError(
|
309
|
+
@"Failed to copy old storage directory to new storage directory location during "
|
310
|
+
@"migration",
|
311
|
+
error);
|
312
|
+
}
|
313
|
+
} else if (shouldCleanupOldDirectory) {
|
314
|
+
// If copying succeeds, remove old storage directory
|
315
|
+
RCTStorageDirectoryCleanupOld(oldDirectoryPath);
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Determine which of RCTOldStorageDirectory or RCTExpoStorageDirectory needs to migrated.
|
321
|
+
* If both exist, we remove the least recently modified and return the most recently modified.
|
322
|
+
* Otherwise, this will return the path to whichever directory exists.
|
323
|
+
* If no directory exists, then return nil.
|
324
|
+
*/
|
325
|
+
static NSString *RCTGetStoragePathForMigration()
|
326
|
+
{
|
327
|
+
BOOL isDir;
|
328
|
+
NSString *oldStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory);
|
329
|
+
NSString *expoStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTExpoStorageDirectory);
|
330
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
331
|
+
BOOL oldStorageDirectoryExists =
|
332
|
+
[fileManager fileExistsAtPath:oldStoragePath isDirectory:&isDir] && isDir;
|
333
|
+
BOOL expoStorageDirectoryExists =
|
334
|
+
[fileManager fileExistsAtPath:expoStoragePath isDirectory:&isDir] && isDir;
|
335
|
+
|
336
|
+
// Check if both the old storage directory and Expo storage directory exist
|
337
|
+
if (oldStorageDirectoryExists && expoStorageDirectoryExists) {
|
338
|
+
// If the old storage has been modified more recently than Expo storage, then clear Expo
|
339
|
+
// storage. Otherwise, clear the old storage.
|
340
|
+
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(oldStoragePath))
|
341
|
+
compare:RCTManifestModificationDate(RCTCreateManifestFilePath(expoStoragePath))] ==
|
342
|
+
NSOrderedDescending) {
|
343
|
+
RCTStorageDirectoryCleanupOld(expoStoragePath);
|
344
|
+
return oldStoragePath;
|
345
|
+
} else {
|
346
|
+
RCTStorageDirectoryCleanupOld(oldStoragePath);
|
347
|
+
return expoStoragePath;
|
348
|
+
}
|
349
|
+
} else if (oldStorageDirectoryExists) {
|
350
|
+
return oldStoragePath;
|
351
|
+
} else if (expoStorageDirectoryExists) {
|
352
|
+
return expoStoragePath;
|
353
|
+
} else {
|
354
|
+
return nil;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
/**
|
359
|
+
* This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
|
360
|
+
* Check that data is migrated from the old location to the new location
|
361
|
+
* fromStorageDirectory: the directory where the older data lives
|
362
|
+
* toStorageDirectory: the directory where the new data should live
|
363
|
+
* shouldCleanupOldDirectoryAndOverwriteNewDirectory: YES if we should delete the old directory's
|
364
|
+
* contents and overwrite the new directory's contents during the migration to the new directory
|
365
|
+
*/
|
366
|
+
static void
|
367
|
+
RCTStorageDirectoryMigrationCheck(NSString *fromStorageDirectory,
|
368
|
+
NSString *toStorageDirectory,
|
369
|
+
BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory)
|
370
|
+
{
|
371
|
+
NSError *error;
|
372
|
+
BOOL isDir;
|
373
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
374
|
+
// If the old directory exists, it means we may need to migrate old data to the new directory
|
375
|
+
if ([fileManager fileExistsAtPath:fromStorageDirectory isDirectory:&isDir] && isDir) {
|
376
|
+
// Check if the new storage directory location already exists
|
377
|
+
if ([fileManager fileExistsAtPath:toStorageDirectory]) {
|
378
|
+
// If new storage location exists, check if the new storage has been modified sooner in
|
379
|
+
// which case we may want to cleanup the old location
|
380
|
+
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(toStorageDirectory))
|
381
|
+
compare:RCTManifestModificationDate(
|
382
|
+
RCTCreateManifestFilePath(fromStorageDirectory))] == 1) {
|
383
|
+
// If new location has been modified more recently, simply clean out old data
|
384
|
+
if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
385
|
+
RCTStorageDirectoryCleanupOld(fromStorageDirectory);
|
386
|
+
}
|
387
|
+
} else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
388
|
+
// If old location has been modified more recently, remove new storage and migrate
|
389
|
+
if (![fileManager removeItemAtPath:toStorageDirectory error:&error]) {
|
390
|
+
RCTStorageDirectoryMigrationLogError(
|
391
|
+
@"Failed to remove new storage directory during migration", error);
|
392
|
+
} else {
|
393
|
+
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
394
|
+
toStorageDirectory,
|
395
|
+
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
396
|
+
}
|
397
|
+
}
|
398
|
+
} else {
|
399
|
+
// If new storage location doesn't exist, migrate data
|
400
|
+
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
401
|
+
toStorageDirectory,
|
402
|
+
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
#pragma mark - RNCAsyncStorage
|
408
|
+
|
409
|
+
@implementation RNCAsyncStorage {
|
410
|
+
BOOL _haveSetup;
|
411
|
+
// The manifest is a dictionary of all keys with small values inlined. Null values indicate
|
412
|
+
// values that are stored in separate files (as opposed to nil values which don't exist). The
|
413
|
+
// manifest is read off disk at startup, and written to disk after all mutations.
|
414
|
+
NSMutableDictionary<NSString *, NSString *> *_manifest;
|
415
|
+
}
|
416
|
+
|
417
|
+
+ (BOOL)requiresMainQueueSetup
|
418
|
+
{
|
419
|
+
return NO;
|
420
|
+
}
|
421
|
+
|
422
|
+
- (instancetype)init
|
423
|
+
{
|
424
|
+
if (!(self = [super init])) {
|
425
|
+
return nil;
|
426
|
+
}
|
427
|
+
|
428
|
+
// Get the path to any old storage directory that needs to be migrated. If multiple exist,
|
429
|
+
// the oldest are removed and the most recently modified is returned.
|
430
|
+
NSString *oldStoragePath = RCTGetStoragePathForMigration();
|
431
|
+
if (oldStoragePath != nil) {
|
432
|
+
// Migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" or
|
433
|
+
// "Documents/.../RCTAsyncLocalStorage" to "Documents/.../RCTAsyncLocalStorage_V1"
|
434
|
+
RCTStorageDirectoryMigrationCheck(
|
435
|
+
oldStoragePath, RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), YES);
|
436
|
+
}
|
437
|
+
|
438
|
+
// Migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to
|
439
|
+
// "Application Support/[bundleID]/RCTAsyncLocalStorage_V1"
|
440
|
+
RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory),
|
441
|
+
RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
442
|
+
NO);
|
443
|
+
|
444
|
+
return self;
|
445
|
+
}
|
446
|
+
|
447
|
+
RCT_EXPORT_MODULE()
|
448
|
+
|
449
|
+
- (dispatch_queue_t)methodQueue
|
450
|
+
{
|
451
|
+
return RCTGetMethodQueue();
|
452
|
+
}
|
453
|
+
|
454
|
+
- (void)clearAllData
|
455
|
+
{
|
456
|
+
dispatch_async(RCTGetMethodQueue(), ^{
|
457
|
+
[self->_manifest removeAllObjects];
|
458
|
+
[RCTGetCache() removeAllObjects];
|
459
|
+
RCTDeleteStorageDirectory();
|
460
|
+
});
|
461
|
+
}
|
462
|
+
|
463
|
+
+ (void)clearAllData
|
464
|
+
{
|
465
|
+
dispatch_async(RCTGetMethodQueue(), ^{
|
466
|
+
[RCTGetCache() removeAllObjects];
|
467
|
+
RCTDeleteStorageDirectory();
|
468
|
+
});
|
469
|
+
}
|
470
|
+
|
471
|
+
- (void)invalidate
|
472
|
+
{
|
473
|
+
if (_clearOnInvalidate) {
|
474
|
+
[RCTGetCache() removeAllObjects];
|
475
|
+
RCTDeleteStorageDirectory();
|
476
|
+
}
|
477
|
+
_clearOnInvalidate = NO;
|
478
|
+
[_manifest removeAllObjects];
|
479
|
+
_haveSetup = NO;
|
480
|
+
}
|
481
|
+
|
482
|
+
- (BOOL)isValid
|
483
|
+
{
|
484
|
+
return _haveSetup;
|
485
|
+
}
|
486
|
+
|
487
|
+
- (void)dealloc
|
488
|
+
{
|
489
|
+
[self invalidate];
|
490
|
+
}
|
491
|
+
|
492
|
+
- (NSString *)_filePathForKey:(NSString *)key
|
493
|
+
{
|
494
|
+
NSString *safeFileName = RCTMD5Hash(key);
|
495
|
+
return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName];
|
496
|
+
}
|
497
|
+
|
498
|
+
- (NSDictionary *)_ensureSetup
|
499
|
+
{
|
500
|
+
RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread");
|
501
|
+
|
502
|
+
#if TARGET_OS_TV
|
503
|
+
RCTLogWarn(
|
504
|
+
@"Persistent storage is not supported on tvOS, your data may be removed at any point.");
|
505
|
+
#endif
|
506
|
+
|
507
|
+
NSError *error = nil;
|
508
|
+
if (!RCTHasCreatedStorageDirectory) {
|
509
|
+
_createStorageDirectory(RCTGetStorageDirectory(), &error);
|
510
|
+
if (error) {
|
511
|
+
return RCTMakeError(@"Failed to create storage directory.", error, nil);
|
512
|
+
}
|
513
|
+
RCTHasCreatedStorageDirectory = YES;
|
514
|
+
}
|
515
|
+
|
516
|
+
if (!_haveSetup) {
|
517
|
+
// iCloud backup exclusion
|
518
|
+
NSNumber *isExcludedFromBackup =
|
519
|
+
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTAsyncStorageExcludeFromBackup"];
|
520
|
+
if (isExcludedFromBackup == nil) {
|
521
|
+
// by default, we want to exclude AsyncStorage data from backup
|
522
|
+
isExcludedFromBackup = @YES;
|
523
|
+
}
|
524
|
+
RCTAsyncStorageSetExcludedFromBackup(RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
525
|
+
isExcludedFromBackup);
|
526
|
+
|
527
|
+
NSDictionary *errorOut = nil;
|
528
|
+
NSString *serialized = RCTReadFile(RCTCreateStorageDirectoryPath(RCTGetManifestFilePath()),
|
529
|
+
RCTManifestFileName,
|
530
|
+
&errorOut);
|
531
|
+
if (!serialized) {
|
532
|
+
if (errorOut) {
|
533
|
+
// We cannot simply create a new manifest in case the file does exist but we have no
|
534
|
+
// access to it. This can happen when data protection is enabled for the app and we
|
535
|
+
// are trying to read the manifest while the device is locked. (The app can be
|
536
|
+
// started by the system even if the device is locked due to e.g. a geofence event.)
|
537
|
+
RCTLogError(
|
538
|
+
@"Could not open the existing manifest, perhaps data protection is "
|
539
|
+
@"enabled?\n\n%@",
|
540
|
+
errorOut);
|
541
|
+
return errorOut;
|
542
|
+
} else {
|
543
|
+
// We can get nil without errors only when the file does not exist.
|
544
|
+
RCTLogTrace(@"Manifest does not exist - creating a new one.\n\n%@", errorOut);
|
545
|
+
_manifest = [NSMutableDictionary new];
|
546
|
+
}
|
547
|
+
} else {
|
548
|
+
_manifest = RCTJSONParseMutable(serialized, &error);
|
549
|
+
if (!_manifest) {
|
550
|
+
RCTLogError(@"Failed to parse manifest - creating a new one.\n\n%@", error);
|
551
|
+
_manifest = [NSMutableDictionary new];
|
552
|
+
}
|
553
|
+
}
|
554
|
+
_haveSetup = YES;
|
555
|
+
}
|
556
|
+
|
557
|
+
return nil;
|
558
|
+
}
|
559
|
+
|
560
|
+
- (NSDictionary *)_writeManifest:(NSMutableArray<NSDictionary *> **)errors
|
561
|
+
{
|
562
|
+
NSError *error;
|
563
|
+
NSString *serialized = RCTJSONStringify(_manifest, &error);
|
564
|
+
[serialized writeToFile:RCTCreateStorageDirectoryPath(RCTGetManifestFilePath())
|
565
|
+
atomically:YES
|
566
|
+
encoding:NSUTF8StringEncoding
|
567
|
+
error:&error];
|
568
|
+
NSDictionary *errorOut;
|
569
|
+
if (error) {
|
570
|
+
errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil);
|
571
|
+
RCTAppendError(errorOut, errors);
|
572
|
+
}
|
573
|
+
return errorOut;
|
574
|
+
}
|
575
|
+
|
576
|
+
- (NSDictionary *)_appendItemForKey:(NSString *)key
|
577
|
+
toArray:(NSMutableArray<NSArray<NSString *> *> *)result
|
578
|
+
{
|
579
|
+
NSDictionary *errorOut = RCTErrorForKey(key);
|
580
|
+
if (errorOut) {
|
581
|
+
return errorOut;
|
582
|
+
}
|
583
|
+
NSString *value = [self _getValueForKey:key errorOut:&errorOut];
|
584
|
+
[result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure.
|
585
|
+
return errorOut;
|
586
|
+
}
|
587
|
+
|
588
|
+
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
|
589
|
+
{
|
590
|
+
NSString *value =
|
591
|
+
_manifest[key]; // nil means missing, null means there may be a data file, else: NSString
|
592
|
+
if (value == (id)kCFNull) {
|
593
|
+
value = [RCTGetCache() objectForKey:key];
|
594
|
+
if (!value) {
|
595
|
+
NSString *filePath = [self _filePathForKey:key];
|
596
|
+
value = RCTReadFile(filePath, key, errorOut);
|
597
|
+
if (value) {
|
598
|
+
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
599
|
+
} else {
|
600
|
+
// file does not exist after all, so remove from manifest (no need to save
|
601
|
+
// manifest immediately though, as cost of checking again next time is negligible)
|
602
|
+
[_manifest removeObjectForKey:key];
|
603
|
+
}
|
604
|
+
}
|
605
|
+
}
|
606
|
+
return value;
|
607
|
+
}
|
608
|
+
|
609
|
+
- (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL *)changedManifest
|
610
|
+
{
|
611
|
+
if (entry.count != 2) {
|
612
|
+
return RCTMakeAndLogError(
|
613
|
+
@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil);
|
614
|
+
}
|
615
|
+
NSString *key = entry[0];
|
616
|
+
NSDictionary *errorOut = RCTErrorForKey(key);
|
617
|
+
if (errorOut) {
|
618
|
+
return errorOut;
|
619
|
+
}
|
620
|
+
NSString *value = entry[1];
|
621
|
+
NSString *filePath = [self _filePathForKey:key];
|
622
|
+
NSError *error;
|
623
|
+
if (value.length <= RCTInlineValueThreshold) {
|
624
|
+
if (_manifest[key] == (id)kCFNull) {
|
625
|
+
// If the value already existed but wasn't inlined, remove the old file.
|
626
|
+
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
627
|
+
[RCTGetCache() removeObjectForKey:key];
|
628
|
+
}
|
629
|
+
*changedManifest = YES;
|
630
|
+
_manifest[key] = value;
|
631
|
+
return nil;
|
632
|
+
}
|
633
|
+
[value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
634
|
+
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
635
|
+
if (error) {
|
636
|
+
errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
|
637
|
+
} else if (_manifest[key] != (id)kCFNull) {
|
638
|
+
*changedManifest = YES;
|
639
|
+
_manifest[key] = (id)kCFNull;
|
640
|
+
}
|
641
|
+
return errorOut;
|
642
|
+
}
|
643
|
+
|
644
|
+
- (void)_multiGet:(NSArray<NSString *> *)keys
|
645
|
+
callback:(RCTResponseSenderBlock)callback
|
646
|
+
getter:(NSString * (^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue
|
647
|
+
{
|
648
|
+
NSMutableArray<NSDictionary *> *errors;
|
649
|
+
NSMutableArray<NSArray<NSString *> *> *result = [NSMutableArray arrayWithCapacity:keys.count];
|
650
|
+
for (NSUInteger i = 0; i < keys.count; ++i) {
|
651
|
+
NSString *key = keys[i];
|
652
|
+
id keyError;
|
653
|
+
id value = getValue(i, key, &keyError);
|
654
|
+
[result addObject:@[key, RCTNullIfNil(value)]];
|
655
|
+
RCTAppendError(keyError, &errors);
|
656
|
+
}
|
657
|
+
callback(@[RCTNullIfNil(errors), result]);
|
658
|
+
}
|
659
|
+
|
660
|
+
- (BOOL)_passthroughDelegate
|
661
|
+
{
|
662
|
+
return
|
663
|
+
[self.delegate respondsToSelector:@selector(isPassthrough)] && self.delegate.isPassthrough;
|
664
|
+
}
|
665
|
+
|
666
|
+
#pragma mark - Exported JS Functions
|
667
|
+
|
668
|
+
// clang-format off
|
669
|
+
RCT_EXPORT_METHOD(multiGet:(NSArray<NSString *> *)keys
|
670
|
+
callback:(RCTResponseSenderBlock)callback)
|
671
|
+
// clang-format on
|
672
|
+
{
|
673
|
+
if (self.delegate != nil) {
|
674
|
+
[self.delegate
|
675
|
+
valuesForKeys:keys
|
676
|
+
completion:^(NSArray<id<NSObject>> *valuesOrErrors) {
|
677
|
+
[self _multiGet:keys
|
678
|
+
callback:callback
|
679
|
+
getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
680
|
+
id valueOrError = valuesOrErrors[i];
|
681
|
+
if ([valueOrError isKindOfClass:[NSError class]]) {
|
682
|
+
NSError *error = (NSError *)valueOrError;
|
683
|
+
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
684
|
+
*errorOut =
|
685
|
+
RCTMakeError(error.localizedDescription, error, extraData);
|
686
|
+
return nil;
|
687
|
+
} else {
|
688
|
+
return [valueOrError isKindOfClass:[NSString class]]
|
689
|
+
? (NSString *)valueOrError
|
690
|
+
: nil;
|
691
|
+
}
|
692
|
+
}];
|
693
|
+
}];
|
694
|
+
|
695
|
+
if (![self _passthroughDelegate]) {
|
696
|
+
return;
|
697
|
+
}
|
698
|
+
}
|
699
|
+
|
700
|
+
NSDictionary *errorOut = [self _ensureSetup];
|
701
|
+
if (errorOut) {
|
702
|
+
callback(@[@[errorOut], (id)kCFNull]);
|
703
|
+
return;
|
704
|
+
}
|
705
|
+
[self _multiGet:keys
|
706
|
+
callback:callback
|
707
|
+
getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
708
|
+
return [self _getValueForKey:key errorOut:errorOut];
|
709
|
+
}];
|
710
|
+
}
|
711
|
+
|
712
|
+
// clang-format off
|
713
|
+
RCT_EXPORT_METHOD(multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
|
714
|
+
callback:(RCTResponseSenderBlock)callback)
|
715
|
+
// clang-format on
|
716
|
+
{
|
717
|
+
if (self.delegate != nil) {
|
718
|
+
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
719
|
+
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
720
|
+
for (NSArray<NSString *> *entry in kvPairs) {
|
721
|
+
[keys addObject:entry[0]];
|
722
|
+
[values addObject:entry[1]];
|
723
|
+
}
|
724
|
+
[self.delegate setValues:values
|
725
|
+
forKeys:keys
|
726
|
+
completion:^(NSArray<id<NSObject>> *results) {
|
727
|
+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
728
|
+
callback(@[RCTNullIfNil(errors)]);
|
729
|
+
}];
|
730
|
+
|
731
|
+
if (![self _passthroughDelegate]) {
|
732
|
+
return;
|
733
|
+
}
|
734
|
+
}
|
735
|
+
|
736
|
+
NSDictionary *errorOut = [self _ensureSetup];
|
737
|
+
if (errorOut) {
|
738
|
+
callback(@[@[errorOut]]);
|
739
|
+
return;
|
740
|
+
}
|
741
|
+
BOOL changedManifest = NO;
|
742
|
+
NSMutableArray<NSDictionary *> *errors;
|
743
|
+
for (NSArray<NSString *> *entry in kvPairs) {
|
744
|
+
NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
745
|
+
RCTAppendError(keyError, &errors);
|
746
|
+
}
|
747
|
+
if (changedManifest) {
|
748
|
+
[self _writeManifest:&errors];
|
749
|
+
}
|
750
|
+
callback(@[RCTNullIfNil(errors)]);
|
751
|
+
}
|
752
|
+
|
753
|
+
// clang-format off
|
754
|
+
RCT_EXPORT_METHOD(multiMerge:(NSArray<NSArray<NSString *> *> *)kvPairs
|
755
|
+
callback:(RCTResponseSenderBlock)callback)
|
756
|
+
// clang-format on
|
757
|
+
{
|
758
|
+
if (self.delegate != nil) {
|
759
|
+
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
760
|
+
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
761
|
+
for (NSArray<NSString *> *entry in kvPairs) {
|
762
|
+
[keys addObject:entry[0]];
|
763
|
+
[values addObject:entry[1]];
|
764
|
+
}
|
765
|
+
[self.delegate mergeValues:values
|
766
|
+
forKeys:keys
|
767
|
+
completion:^(NSArray<id<NSObject>> *results) {
|
768
|
+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
769
|
+
callback(@[RCTNullIfNil(errors)]);
|
770
|
+
}];
|
771
|
+
|
772
|
+
if (![self _passthroughDelegate]) {
|
773
|
+
return;
|
774
|
+
}
|
775
|
+
}
|
776
|
+
|
777
|
+
NSDictionary *errorOut = [self _ensureSetup];
|
778
|
+
if (errorOut) {
|
779
|
+
callback(@[@[errorOut]]);
|
780
|
+
return;
|
781
|
+
}
|
782
|
+
BOOL changedManifest = NO;
|
783
|
+
NSMutableArray<NSDictionary *> *errors;
|
784
|
+
for (__strong NSArray<NSString *> *entry in kvPairs) {
|
785
|
+
NSDictionary *keyError;
|
786
|
+
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
|
787
|
+
if (!keyError) {
|
788
|
+
if (value) {
|
789
|
+
NSError *jsonError;
|
790
|
+
NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError);
|
791
|
+
if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) {
|
792
|
+
entry = @[entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL))];
|
793
|
+
}
|
794
|
+
if (jsonError) {
|
795
|
+
keyError = RCTJSErrorFromNSError(jsonError);
|
796
|
+
}
|
797
|
+
}
|
798
|
+
if (!keyError) {
|
799
|
+
keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
800
|
+
}
|
801
|
+
}
|
802
|
+
RCTAppendError(keyError, &errors);
|
803
|
+
}
|
804
|
+
if (changedManifest) {
|
805
|
+
[self _writeManifest:&errors];
|
806
|
+
}
|
807
|
+
callback(@[RCTNullIfNil(errors)]);
|
808
|
+
}
|
809
|
+
|
810
|
+
// clang-format off
|
811
|
+
RCT_EXPORT_METHOD(multiRemove:(NSArray<NSString *> *)keys
|
812
|
+
callback:(RCTResponseSenderBlock)callback)
|
813
|
+
// clang-format on
|
814
|
+
{
|
815
|
+
if (self.delegate != nil) {
|
816
|
+
[self.delegate removeValuesForKeys:keys
|
817
|
+
completion:^(NSArray<id<NSObject>> *results) {
|
818
|
+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
819
|
+
callback(@[RCTNullIfNil(errors)]);
|
820
|
+
}];
|
821
|
+
|
822
|
+
if (![self _passthroughDelegate]) {
|
823
|
+
return;
|
824
|
+
}
|
825
|
+
}
|
826
|
+
|
827
|
+
NSDictionary *errorOut = [self _ensureSetup];
|
828
|
+
if (errorOut) {
|
829
|
+
callback(@[@[errorOut]]);
|
830
|
+
return;
|
831
|
+
}
|
832
|
+
NSMutableArray<NSDictionary *> *errors;
|
833
|
+
BOOL changedManifest = NO;
|
834
|
+
for (NSString *key in keys) {
|
835
|
+
NSDictionary *keyError = RCTErrorForKey(key);
|
836
|
+
if (!keyError) {
|
837
|
+
if (_manifest[key] == (id)kCFNull) {
|
838
|
+
NSString *filePath = [self _filePathForKey:key];
|
839
|
+
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
840
|
+
[RCTGetCache() removeObjectForKey:key];
|
841
|
+
}
|
842
|
+
if (_manifest[key]) {
|
843
|
+
changedManifest = YES;
|
844
|
+
[_manifest removeObjectForKey:key];
|
845
|
+
}
|
846
|
+
}
|
847
|
+
RCTAppendError(keyError, &errors);
|
848
|
+
}
|
849
|
+
if (changedManifest) {
|
850
|
+
[self _writeManifest:&errors];
|
851
|
+
}
|
852
|
+
callback(@[RCTNullIfNil(errors)]);
|
853
|
+
}
|
854
|
+
|
855
|
+
// clang-format off
|
856
|
+
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
|
857
|
+
// clang-format on
|
858
|
+
{
|
859
|
+
if (self.delegate != nil) {
|
860
|
+
[self.delegate removeAllValues:^(NSError *error) {
|
861
|
+
NSDictionary *result = nil;
|
862
|
+
if (error != nil) {
|
863
|
+
result = RCTMakeError(error.localizedDescription, error, nil);
|
864
|
+
}
|
865
|
+
callback(@[RCTNullIfNil(result)]);
|
866
|
+
}];
|
867
|
+
return;
|
868
|
+
}
|
869
|
+
|
870
|
+
[_manifest removeAllObjects];
|
871
|
+
[RCTGetCache() removeAllObjects];
|
872
|
+
NSDictionary *error = RCTDeleteStorageDirectory();
|
873
|
+
callback(@[RCTNullIfNil(error)]);
|
874
|
+
}
|
875
|
+
|
876
|
+
// clang-format off
|
877
|
+
RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback)
|
878
|
+
// clang-format on
|
879
|
+
{
|
880
|
+
if (self.delegate != nil) {
|
881
|
+
[self.delegate allKeys:^(NSArray<id<NSObject>> *keys) {
|
882
|
+
callback(@[(id)kCFNull, keys]);
|
883
|
+
}];
|
884
|
+
|
885
|
+
if (![self _passthroughDelegate]) {
|
886
|
+
return;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
|
890
|
+
NSDictionary *errorOut = [self _ensureSetup];
|
891
|
+
if (errorOut) {
|
892
|
+
callback(@[errorOut, (id)kCFNull]);
|
893
|
+
} else {
|
894
|
+
callback(@[(id)kCFNull, _manifest.allKeys]);
|
895
|
+
}
|
896
|
+
}
|
897
|
+
|
898
|
+
@end
|