@fto-consult/expo-ui 6.37.3 → 6.37.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/node_modules/.package-lock.json +26218 -26188
  2. package/node_modules/@react-native-async-storage/async-storage/LICENSE +21 -0
  3. package/node_modules/@react-native-async-storage/async-storage/README.md +27 -0
  4. package/node_modules/@react-native-async-storage/async-storage/RNCAsyncStorage.podspec +19 -0
  5. package/node_modules/@react-native-async-storage/async-storage/android/build.gradle +142 -0
  6. package/node_modules/@react-native-async-storage/async-storage/android/src/main/AndroidManifest.xml +6 -0
  7. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +178 -0
  8. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageErrorUtil.java +45 -0
  9. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageExpoMigration.java +154 -0
  10. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +424 -0
  11. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +58 -0
  12. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/ReactDatabaseSupplier.java +163 -0
  13. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/SerialExecutor.java +40 -0
  14. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpers.kt +86 -0
  15. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ErrorHelpers.kt +39 -0
  16. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +90 -0
  17. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +161 -0
  18. package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpersTest.kt +93 -0
  19. package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/StorageTest.kt +141 -0
  20. package/node_modules/@react-native-async-storage/async-storage/android/testresults.gradle +38 -0
  21. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.h +51 -0
  22. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.m +898 -0
  23. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.xcodeproj/project.pbxproj +283 -0
  24. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorageDelegate.h +73 -0
  25. package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.d.ts +9 -0
  26. package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.js +109 -0
  27. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js +164 -0
  28. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js.map +1 -0
  29. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js +366 -0
  30. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js.map +1 -0
  31. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js +30 -0
  32. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js.map +1 -0
  33. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js +69 -0
  34. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js.map +1 -0
  35. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js +44 -0
  36. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js.map +1 -0
  37. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js +22 -0
  38. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js.map +1 -0
  39. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js +39 -0
  40. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js.map +1 -0
  41. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js +2 -0
  42. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js.map +1 -0
  43. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js +153 -0
  44. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js.map +1 -0
  45. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js +348 -0
  46. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js.map +1 -0
  47. package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js +20 -0
  48. package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js.map +1 -0
  49. package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js +56 -0
  50. package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js.map +1 -0
  51. package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js +34 -0
  52. package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js.map +1 -0
  53. package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js +4 -0
  54. package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js.map +1 -0
  55. package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js +31 -0
  56. package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js.map +1 -0
  57. package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js +2 -0
  58. package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js.map +1 -0
  59. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.d.ts +10 -0
  60. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.native.d.ts +16 -0
  61. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/RCTAsyncStorage.d.ts +2 -0
  62. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/helpers.d.ts +5 -0
  63. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/hooks.d.ts +2 -0
  64. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/index.d.ts +4 -0
  65. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/shouldFallbackToLegacyNativeModule.d.ts +1 -0
  66. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/types.d.ts +113 -0
  67. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/project.pbxproj +385 -0
  68. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage-macOS.xcscheme +67 -0
  69. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage.xcscheme +67 -0
  70. package/node_modules/@react-native-async-storage/async-storage/package.json +197 -0
  71. package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.native.ts +356 -0
  72. package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts +173 -0
  73. package/node_modules/@react-native-async-storage/async-storage/src/RCTAsyncStorage.ts +28 -0
  74. package/node_modules/@react-native-async-storage/async-storage/src/helpers.ts +74 -0
  75. package/node_modules/@react-native-async-storage/async-storage/src/hooks.ts +11 -0
  76. package/node_modules/@react-native-async-storage/async-storage/src/index.ts +7 -0
  77. package/node_modules/@react-native-async-storage/async-storage/src/shouldFallbackToLegacyNativeModule.ts +34 -0
  78. package/node_modules/@react-native-async-storage/async-storage/src/types.ts +155 -0
  79. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/PropertySheet.props +16 -0
  80. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj +172 -0
  81. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj.filters +34 -0
  82. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/packages.config +4 -0
  83. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage.sln +172 -0
  84. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/PropertySheet.props +16 -0
  85. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj +157 -0
  86. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj.filters +34 -0
  87. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/packages.config +4 -0
  88. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61.sln +195 -0
  89. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage62.sln +192 -0
  90. package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.cpp +599 -0
  91. package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.h +162 -0
  92. package/node_modules/@react-native-async-storage/async-storage/windows/code/RNCAsyncStorage.h +118 -0
  93. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactNativeAsyncStorage.def +3 -0
  94. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.cpp +20 -0
  95. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.h +23 -0
  96. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.idl +7 -0
  97. package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.cpp +3 -0
  98. package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.h +15 -0
  99. package/node_modules/merge-options/index.d.ts +2 -0
  100. package/node_modules/merge-options/index.js +171 -0
  101. package/node_modules/merge-options/index.mjs +8 -0
  102. package/node_modules/merge-options/license +21 -0
  103. package/node_modules/merge-options/node_modules/is-plain-obj/index.d.ts +29 -0
  104. package/node_modules/merge-options/node_modules/is-plain-obj/index.js +10 -0
  105. package/node_modules/merge-options/node_modules/is-plain-obj/license +9 -0
  106. package/node_modules/merge-options/node_modules/is-plain-obj/package.json +38 -0
  107. package/node_modules/merge-options/node_modules/is-plain-obj/readme.md +54 -0
  108. package/node_modules/merge-options/package.json +59 -0
  109. package/node_modules/merge-options/readme.md +130 -0
  110. package/package.json +131 -130
@@ -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