diff --git a/React/CoreModules/BUCK b/React/CoreModules/BUCK index 5608bacdaea..143a840eff4 100644 --- a/React/CoreModules/BUCK +++ b/React/CoreModules/BUCK @@ -78,9 +78,6 @@ rn_apple_library( ) + react_module_plugin_providers( name = "AlertManager", native_class_func = "RCTAlertManagerCls", - ) + react_module_plugin_providers( - name = "AsyncLocalStorage", - native_class_func = "RCTAsyncLocalStorageCls", ) + react_module_plugin_providers( name = "Timing", native_class_func = "RCTTimingCls", diff --git a/React/CoreModules/CoreModulesPlugins.h b/React/CoreModules/CoreModulesPlugins.h index 632fd5aeab1..e2a02a4e335 100644 --- a/React/CoreModules/CoreModulesPlugins.h +++ b/React/CoreModules/CoreModulesPlugins.h @@ -39,7 +39,6 @@ Class RCTI18nManagerCls(void) __attribute__((used)); Class RCTSourceCodeCls(void) __attribute__((used)); Class RCTActionSheetManagerCls(void) __attribute__((used)); Class RCTAlertManagerCls(void) __attribute__((used)); -Class RCTAsyncLocalStorageCls(void) __attribute__((used)); Class RCTTimingCls(void) __attribute__((used)); Class RCTStatusBarManagerCls(void) __attribute__((used)); Class RCTKeyboardObserverCls(void) __attribute__((used)); diff --git a/React/CoreModules/CoreModulesPlugins.mm b/React/CoreModules/CoreModulesPlugins.mm index 8915f029c3f..85187305e90 100644 --- a/React/CoreModules/CoreModulesPlugins.mm +++ b/React/CoreModules/CoreModulesPlugins.mm @@ -29,7 +29,6 @@ Class RCTCoreModulesClassProvider(const char *name) { {"SourceCode", RCTSourceCodeCls}, {"ActionSheetManager", RCTActionSheetManagerCls}, {"AlertManager", RCTAlertManagerCls}, - {"AsyncLocalStorage", RCTAsyncLocalStorageCls}, {"Timing", RCTTimingCls}, {"StatusBarManager", RCTStatusBarManagerCls}, {"KeyboardObserver", RCTKeyboardObserverCls}, diff --git a/React/CoreModules/RCTAsyncLocalStorage.h b/React/CoreModules/RCTAsyncLocalStorage.h deleted file mode 100644 index 66136c87110..00000000000 --- a/React/CoreModules/RCTAsyncLocalStorage.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -/** - * A simple, asynchronous, persistent, key-value storage system designed as a - * backend to the AsyncStorage JS module, which is modeled after LocalStorage. - * - * Current implementation stores small values in serialized dictionary and - * larger values in separate files. Since we use a serial file queue - * `RKFileQueue`, reading/writing from multiple threads should be perceived as - * being atomic, unless someone bypasses the `RCTAsyncLocalStorage` API. - * - * Keys and values must always be strings or an error is returned. - */ -@interface RCTAsyncLocalStorage : NSObject - -@property (nonatomic, assign) BOOL clearOnInvalidate; - -@property (nonatomic, readonly, getter=isValid) BOOL valid; - -// Clear the RCTAsyncLocalStorage data from native code -- (void)clearAllData; - -// For clearing data when the bridge may not exist, e.g. when logging out. -+ (void)clearAllData; - -// Grab data from the cache. ResponseBlock result array will have an error at position 0, and an array of arrays at -// position 1. -- (void)multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback; - -// Add multiple key value pairs to the cache. -- (void)multiSet:(NSArray *> *)kvPairs callback:(RCTResponseSenderBlock)callback; - -@end diff --git a/React/CoreModules/RCTAsyncLocalStorage.mm b/React/CoreModules/RCTAsyncLocalStorage.mm deleted file mode 100644 index 326ea78f8d6..00000000000 --- a/React/CoreModules/RCTAsyncLocalStorage.mm +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTAsyncLocalStorage.h" - -#import - -#import -#import -#import - -#import -#import -#import - -#import "CoreModulesPlugins.h" - -static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1"; -static NSString *const RCTManifestFileName = @"manifest.json"; -static const NSUInteger RCTInlineValueThreshold = 1024; - -#pragma mark - Static helper functions - -static NSDictionary *RCTErrorForKey(NSString *key) -{ - if (![key isKindOfClass:[NSString class]]) { - return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key" : key}); - } else if (key.length < 1) { - return RCTMakeAndLogError(@"Invalid key - must be at least one character. Key: ", key, @{@"key" : key}); - } else { - return nil; - } -} - -static void RCTAppendError(NSDictionary *error, NSMutableArray **errors) -{ - if (error && errors) { - if (!*errors) { - *errors = [NSMutableArray new]; - } - [*errors addObject:error]; - } -} - -static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut) -{ - if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { - NSError *error; - NSStringEncoding encoding; - NSString *entryString = [NSString stringWithContentsOfFile:filePath usedEncoding:&encoding error:&error]; - NSDictionary *extraData = @{@"key" : RCTNullIfNil(key)}; - - if (error) { - if (errorOut) - *errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData); - return nil; - } - - if (encoding != NSUTF8StringEncoding) { - if (errorOut) - *errorOut = RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData); - return nil; - } - return entryString; - } - - return nil; -} - -static NSString *RCTGetStorageDirectory() -{ - static NSString *storageDirectory = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; - storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory]; - }); - return storageDirectory; -} - -static NSString *RCTGetManifestFilePath() -{ - static NSString *manifestFilePath = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName]; - }); - return manifestFilePath; -} - -// Only merges objects - all other types are just clobbered (including arrays) -static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source) -{ - BOOL modified = NO; - for (NSString *key in source) { - id sourceValue = source[key]; - id destinationValue = destination[key]; - if ([sourceValue isKindOfClass:[NSDictionary class]]) { - if ([destinationValue isKindOfClass:[NSDictionary class]]) { - if ([destinationValue classForCoder] != [NSMutableDictionary class]) { - destinationValue = [destinationValue mutableCopy]; - } - if (RCTMergeRecursive(destinationValue, sourceValue)) { - destination[key] = destinationValue; - modified = YES; - } - } else { - destination[key] = [sourceValue copy]; - modified = YES; - } - } else if (![source isEqual:destinationValue]) { - destination[key] = [sourceValue copy]; - modified = YES; - } - } - return modified; -} - -static dispatch_queue_t RCTGetMethodQueue() -{ - // We want all instances to share the same queue since they will be reading/writing the same files. - static dispatch_queue_t queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL); - }); - return queue; -} - -static NSCache *RCTGetCache() -{ - // We want all instances to share the same cache since they will be reading/writing the same files. - static NSCache *cache; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cache = [NSCache new]; - cache.totalCostLimit = 2 * 1024 * 1024; // 2MB - - // Clear cache in the event of a memory warning - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification - object:nil - queue:nil - usingBlock:^(__unused NSNotification *note) { - [cache removeAllObjects]; - }]; - }); - return cache; -} - -static BOOL RCTHasCreatedStorageDirectory = NO; -static NSDictionary *RCTDeleteStorageDirectory() -{ - NSError *error; - [[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]; - RCTHasCreatedStorageDirectory = NO; - return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil; -} - -#pragma mark - RCTAsyncLocalStorage - -@interface RCTAsyncLocalStorage () -@end - -@implementation RCTAsyncLocalStorage { - BOOL _haveSetup; - // The manifest is a dictionary of all keys with small values inlined. Null values indicate values that are stored - // in separate files (as opposed to nil values which don't exist). The manifest is read off disk at startup, and - // written to disk after all mutations. - NSMutableDictionary *_manifest; -} - -RCT_EXPORT_MODULE() - -- (dispatch_queue_t)methodQueue -{ - return RCTGetMethodQueue(); -} - -- (void)clearAllData -{ - dispatch_async(RCTGetMethodQueue(), ^{ - [self->_manifest removeAllObjects]; - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - }); -} - -+ (void)clearAllData -{ - dispatch_async(RCTGetMethodQueue(), ^{ - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - }); -} - -- (void)invalidate -{ - if (_clearOnInvalidate) { - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - } - _clearOnInvalidate = NO; - [_manifest removeAllObjects]; - _haveSetup = NO; -} - -- (BOOL)isValid -{ - return _haveSetup; -} - -- (void)dealloc -{ - [self invalidate]; -} - -- (NSString *)_filePathForKey:(NSString *)key -{ - NSString *safeFileName = RCTMD5Hash(key); - return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName]; -} - -- (NSDictionary *)_ensureSetup -{ - RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread"); - - NSError *error = nil; - if (!RCTHasCreatedStorageDirectory) { - [[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory() - withIntermediateDirectories:YES - attributes:nil - error:&error]; - if (error) { - return RCTMakeError(@"Failed to create storage directory.", error, nil); - } - RCTHasCreatedStorageDirectory = YES; - } - if (!_haveSetup) { - NSDictionary *errorOut; - NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), RCTManifestFileName, &errorOut); - _manifest = serialized ? RCTJSONParseMutable(serialized, &error) : [NSMutableDictionary new]; - if (error) { - RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error); - _manifest = [NSMutableDictionary new]; - } - _haveSetup = YES; - } - return nil; -} - -- (NSDictionary *)_writeManifest:(NSMutableArray **)errors -{ - NSError *error; - NSString *serialized = RCTJSONStringify(_manifest, &error); - [serialized writeToFile:RCTGetManifestFilePath() atomically:YES encoding:NSUTF8StringEncoding error:&error]; - NSDictionary *errorOut; - if (error) { - errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil); - RCTAppendError(errorOut, errors); - } - return errorOut; -} - -- (NSDictionary *)_appendItemForKey:(NSString *)key toArray:(NSMutableArray *> *)result -{ - NSDictionary *errorOut = RCTErrorForKey(key); - if (errorOut) { - return errorOut; - } - NSString *value = [self _getValueForKey:key errorOut:&errorOut]; - [result addObject:@[ key, RCTNullIfNil(value) ]]; // Insert null if missing or failure. - return errorOut; -} - -- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut -{ - NSString *value = _manifest[key]; // nil means missing, null means there may be a data file, else: NSString - if (value == (id)kCFNull) { - value = [RCTGetCache() objectForKey:key]; - if (!value) { - NSString *filePath = [self _filePathForKey:key]; - value = RCTReadFile(filePath, key, errorOut); - if (value) { - [RCTGetCache() setObject:value forKey:key cost:value.length]; - } else { - // file does not exist after all, so remove from manifest (no need to save - // manifest immediately though, as cost of checking again next time is negligible) - [_manifest removeObjectForKey:key]; - } - } - } - return value; -} - -- (NSDictionary *)_writeEntry:(NSArray *)entry changedManifest:(BOOL *)changedManifest -{ - if (entry.count != 2) { - return RCTMakeAndLogError(@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil); - } - NSString *key = entry[0]; - NSDictionary *errorOut = RCTErrorForKey(key); - if (errorOut) { - return errorOut; - } - if (![entry[1] isKindOfClass:[NSString class]]) { - return RCTMakeAndLogError(@"Invalid value for entry - must be a string. Got entry: ", entry, nil); - } - NSString *value = entry[1]; - - NSString *filePath = [self _filePathForKey:key]; - NSError *error; - if (value.length <= RCTInlineValueThreshold) { - if (_manifest[key] == (id)kCFNull) { - // If the value already existed but wasn't inlined, remove the old file. - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - [RCTGetCache() removeObjectForKey:key]; - } - *changedManifest = YES; - _manifest[key] = value; - return nil; - } - [value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]; - [RCTGetCache() setObject:value forKey:key cost:value.length]; - if (error) { - errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key" : key}); - } else if (_manifest[key] != (id)kCFNull) { - *changedManifest = YES; - _manifest[key] = (id)kCFNull; - } - return errorOut; -} - -#pragma mark - Exported JS Functions - -RCT_EXPORT_METHOD(multiGet : (NSArray *)keys callback : (RCTResponseSenderBlock)callback) -{ - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[ @[ errorOut ], (id)kCFNull ]); - return; - } - NSMutableArray *errors; - NSMutableArray *> *result = [[NSMutableArray alloc] initWithCapacity:keys.count]; - for (NSString *key in keys) { - id keyError; - id value = [self _getValueForKey:key errorOut:&keyError]; - [result addObject:@[ key, RCTNullIfNil(value) ]]; - RCTAppendError(keyError, &errors); - } - callback(@[ RCTNullIfNil(errors), result ]); -} - -RCT_EXPORT_METHOD(multiSet : (NSArray *> *)kvPairs callback : (RCTResponseSenderBlock)callback) -{ - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[ @[ errorOut ] ]); - return; - } - BOOL changedManifest = NO; - NSMutableArray *errors; - for (NSArray *entry in kvPairs) { - NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest]; - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[ RCTNullIfNil(errors) ]); -} - -RCT_EXPORT_METHOD(multiMerge : (NSArray *> *)kvPairs callback : (RCTResponseSenderBlock)callback) -{ - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[ @[ errorOut ] ]); - return; - } - BOOL changedManifest = NO; - NSMutableArray *errors; - for (__strong NSArray *entry in kvPairs) { - NSDictionary *keyError; - NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError]; - if (!keyError) { - if (value) { - NSError *jsonError; - NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError); - if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) { - entry = @[ entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL)) ]; - } - if (jsonError) { - keyError = RCTJSErrorFromNSError(jsonError); - } - } - if (!keyError) { - keyError = [self _writeEntry:entry changedManifest:&changedManifest]; - } - } - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[ RCTNullIfNil(errors) ]); -} - -RCT_EXPORT_METHOD(multiRemove : (NSArray *)keys callback : (RCTResponseSenderBlock)callback) -{ - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[ @[ errorOut ] ]); - return; - } - NSMutableArray *errors; - BOOL changedManifest = NO; - for (NSString *key in keys) { - NSDictionary *keyError = RCTErrorForKey(key); - if (!keyError) { - if (_manifest[key] == (id)kCFNull) { - NSString *filePath = [self _filePathForKey:key]; - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - [RCTGetCache() removeObjectForKey:key]; - } - if (_manifest[key]) { - changedManifest = YES; - [_manifest removeObjectForKey:key]; - } - } - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[ RCTNullIfNil(errors) ]); -} - -RCT_EXPORT_METHOD(clear : (RCTResponseSenderBlock)callback) -{ - [_manifest removeAllObjects]; - [RCTGetCache() removeAllObjects]; - NSDictionary *error = RCTDeleteStorageDirectory(); - callback(@[ RCTNullIfNil(error) ]); -} - -RCT_EXPORT_METHOD(getAllKeys : (RCTResponseSenderBlock)callback) -{ - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[ errorOut, (id)kCFNull ]); - } else { - callback(@[ (id)kCFNull, _manifest.allKeys ]); - } -} - -- (std::shared_ptr)getTurboModule: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return std::make_shared(params); -} - -@end - -Class RCTAsyncLocalStorageCls(void) -{ - return RCTAsyncLocalStorage.class; -} diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index fd7b6b9e62e..4ed5001f3da 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -929,7 +929,7 @@ EXTERNAL SOURCES: :path: "../../ReactCommon/yoga" SPEC CHECKSUMS: - boost: a7c83b31436843459a1961bfd74b96033dc77234 + boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: 19e408e76fa9258dd32191a50d60c41444f52d29 @@ -948,7 +948,7 @@ SPEC CHECKSUMS: hermes-engine: cd15ebd246edff3a995ec666e898dd1cbdcaa10d libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: 1c8808cf84569265784a6c33984bbb506ada8c6e RCTTypeSafety: b6dcb5036a808864ee8cad66ca15f263c24661cc React: 8d809d414723bb5763093ddec7658066a21ccabc @@ -957,15 +957,15 @@ SPEC CHECKSUMS: React-Codegen: d2434d5e4d238bceef25f40c4f58b199eb981ad0 React-Core: 3965263aa4b4e1ebf7b4fdb50d2f49ce7bf28f63 React-CoreModules: 675170bccf156da3a3348e04e2036ce401b2010d - React-cxxreact: 7276467c246302fedf598cc40d7003896ddb20ba + React-cxxreact: ebed982230716c3515ab2f435cb13aec8a56af02 React-Fabric: 141459e61c825acf02d26ece099acbd9cbd87b99 - React-graphics: 5ccc9cc0d91794fd42bc1c693e9aea207554bbef + React-graphics: 2dda97baebb0082bb85499c862c3f269a194f416 React-hermes: 0a5145bae4207edf0def8e28fbcb6a8fd6e806c2 React-jsi: c24dbcfdf7ea075138b73372387c7f17c0db56ef React-jsidynamic: 2b14ac1b6d3a1b7daa1e5a424b98de87da981698 React-jsiexecutor: 14e899380e3fe9ca74c4e19727540a03e7574721 React-jsinspector: 7733dd522d044aef87caa39f3eda77593358a7eb - React-logger: c7960346b021767ed90971aff592a44e3d69f8bb + React-logger: a2b4f0f204e45e25f493ca4ce245a336217c923e React-perflogger: c4fdd48988c2d3047186fc1bc1772d634cfca2ea React-RCTActionSheet: 166fd1df85ac10219466b45d12a5884d3eaceac1 React-RCTAnimation: d6127046c6bb44bd3e67b7503c4ad7f91131b58e @@ -977,13 +977,13 @@ SPEC CHECKSUMS: React-RCTNetwork: a865deadacbf6b3d863f0496e7d2c2e81c269317 React-RCTPushNotification: 7f678a88147254ede5d21a1e1e71e8a964dd0051 React-RCTSettings: 23ce1aa52ddf5db44c973bb5cc93713e871e09b6 - React-RCTTest: 06c388632dc7b30df17af01c8f9e89e641b4d31c + React-RCTTest: be92171ef0a1818f96324eac3be0356f4fa08844 React-RCTText: a861fbf2835299d3cc4189697cddd8bd8602afb9 React-RCTVibration: 0386f50996a153b3f39cecbe7d139763ac9a9fdf React-rncore: 665c70690f404bbfa3948148de72689672a906d2 React-runtimeexecutor: 97dca9247f4d3cfe0733384b189c6930fbd402b7 ReactCommon: b1f213aa09e3dfd0a89389b5023fdb1cd6528e96 - ScreenshotManager: 3fc534a218e7b8dde632158411d0f15b0ca8893c + ScreenshotManager: 06cb3d1794c8082d92b3e91813d1678d0977a4fb SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 Yoga: 1b1a12ff3d86a10565ea7cbe057d42f5e5fb2a07 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a