From b202e24491c633b17511822b570b137df8796ef6 Mon Sep 17 00:00:00 2001 From: nikki93 Date: Wed, 29 Mar 2017 23:45:40 -0700 Subject: [PATCH] [ios] Let RCTAsyncLocalStorage take storage directory as parameter --- React/Modules/RCTAsyncLocalStorage.h | 6 +- React/Modules/RCTAsyncLocalStorage.m | 126 +++++++++++---------------- 2 files changed, 55 insertions(+), 77 deletions(-) diff --git a/React/Modules/RCTAsyncLocalStorage.h b/React/Modules/RCTAsyncLocalStorage.h index cbb8cb42bed406..936c9016b148db 100644 --- a/React/Modules/RCTAsyncLocalStorage.h +++ b/React/Modules/RCTAsyncLocalStorage.h @@ -27,10 +27,10 @@ @property (nonatomic, readonly, getter=isValid) BOOL valid; +// NOTE(nikki): Added to allow scoped per Expo app +- (instancetype)initWithStorageDirectory:(NSString *)storageDirectory; + // 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; - @end diff --git a/React/Modules/RCTAsyncLocalStorage.m b/React/Modules/RCTAsyncLocalStorage.m index 34442779bea5e1..1696af23700764 100644 --- a/React/Modules/RCTAsyncLocalStorage.m +++ b/React/Modules/RCTAsyncLocalStorage.m @@ -62,31 +62,6 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray * return nil; } -static NSString *RCTGetStorageDirectory() -{ - static NSString *storageDirectory = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ -#if TARGET_OS_TV - storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; -#else - storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; -#endif - 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) { @@ -115,45 +90,35 @@ static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *so 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; -} +// NOTE(nikki93): We replace with scoped implementations of: +// RCTGetStorageDirectory() +// RCTGetManifestFilePath() +// RCTGetMethodQueue() +// RCTGetCache() +// RCTDeleteStorageDirectory() -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; -} +#define RCTGetStorageDirectory() _storageDirectory +#define RCTGetManifestFilePath() _manifestFilePath +#define RCTGetMethodQueue() self.methodQueue +#define RCTGetCache() self.cache -static BOOL RCTHasCreatedStorageDirectory = NO; -static NSDictionary *RCTDeleteStorageDirectory() +static NSDictionary *RCTDeleteStorageDirectory(NSString *storageDirectory) { NSError *error; - [[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]; - RCTHasCreatedStorageDirectory = NO; + [[NSFileManager defaultManager] removeItemAtPath:storageDirectory error:&error]; return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil; } +#define RCTDeleteStorageDirectory() RCTDeleteStorageDirectory(_storageDirectory) #pragma mark - RCTAsyncLocalStorage +@interface RCTAsyncLocalStorage () + +@property (nonatomic, copy) NSString *storageDirectory; +@property (nonatomic, copy) NSString *manifestFilePath; + +@end + @implementation RCTAsyncLocalStorage { BOOL _haveSetup; @@ -161,27 +126,42 @@ @implementation RCTAsyncLocalStorage // 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; + NSCache *_cache; + dispatch_once_t *_cacheOnceToken; } -RCT_EXPORT_MODULE() - -- (dispatch_queue_t)methodQueue +// NOTE(nikki93): Prevents the module from being auto-initialized and allows us to pass our own `storageDirectory` ++ (NSString *)moduleName { return @"RCTAsyncLocalStorage"; } +- (instancetype)initWithStorageDirectory:(NSString *)storageDirectory { - return RCTGetMethodQueue(); + if ((self = [super init])) { + _storageDirectory = storageDirectory; + _manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName]; + } + return self; } -- (void)clearAllData +// NOTE(nikki93): Use the default `methodQueue` since instances have different storage directories +@synthesize methodQueue = _methodQueue; + +- (NSCache *)cache { - dispatch_async(RCTGetMethodQueue(), ^{ - [self->_manifest removeAllObjects]; - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); + dispatch_once(&_cacheOnceToken, ^{ + _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; } -+ (void)clearAllData +- (void)clearAllData { dispatch_async(RCTGetMethodQueue(), ^{ + [self->_manifest removeAllObjects]; [RCTGetCache() removeAllObjects]; RCTDeleteStorageDirectory(); }); @@ -223,15 +203,13 @@ - (NSDictionary *)_ensureSetup #endif 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; + // NOTE(nikki93): `withIntermediateDirectories:YES` makes this idempotent + [[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory() + withIntermediateDirectories:YES + attributes:nil + error:&error]; + if (error) { + return RCTMakeError(@"Failed to create storage directory.", error, nil); } if (!_haveSetup) { NSDictionary *errorOut;