Skip to content

Commit 01c62a6

Browse files
committed
[expo] Let RCTAsyncLocalStorage take storage directory as parameter
1 parent ab0111e commit 01c62a6

File tree

2 files changed

+55
-77
lines changed

2 files changed

+55
-77
lines changed

React/Modules/RCTAsyncLocalStorage.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727

2828
@property (nonatomic, readonly, getter=isValid) BOOL valid;
2929

30+
// NOTE(nikki): Added to allow scoped per Expo app
31+
- (instancetype)initWithStorageDirectory:(NSString *)storageDirectory;
32+
3033
// Clear the RCTAsyncLocalStorage data from native code
3134
- (void)clearAllData;
3235

33-
// For clearing data when the bridge may not exist, e.g. when logging out.
34-
+ (void)clearAllData;
35-
3636
@end

React/Modules/RCTAsyncLocalStorage.m

Lines changed: 52 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -62,31 +62,6 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> *
6262
return nil;
6363
}
6464

65-
static NSString *RCTGetStorageDirectory()
66-
{
67-
static NSString *storageDirectory = nil;
68-
static dispatch_once_t onceToken;
69-
dispatch_once(&onceToken, ^{
70-
#if TARGET_OS_TV
71-
storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
72-
#else
73-
storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
74-
#endif
75-
storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory];
76-
});
77-
return storageDirectory;
78-
}
79-
80-
static NSString *RCTGetManifestFilePath()
81-
{
82-
static NSString *manifestFilePath = nil;
83-
static dispatch_once_t onceToken;
84-
dispatch_once(&onceToken, ^{
85-
manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName];
86-
});
87-
return manifestFilePath;
88-
}
89-
9065
// Only merges objects - all other types are just clobbered (including arrays)
9166
static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
9267
{
@@ -115,73 +90,78 @@ static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *so
11590
return modified;
11691
}
11792

118-
static dispatch_queue_t RCTGetMethodQueue()
119-
{
120-
// We want all instances to share the same queue since they will be reading/writing the same files.
121-
static dispatch_queue_t queue;
122-
static dispatch_once_t onceToken;
123-
dispatch_once(&onceToken, ^{
124-
queue = dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
125-
});
126-
return queue;
127-
}
93+
// NOTE(nikki93): We replace with scoped implementations of:
94+
// RCTGetStorageDirectory()
95+
// RCTGetManifestFilePath()
96+
// RCTGetMethodQueue()
97+
// RCTGetCache()
98+
// RCTDeleteStorageDirectory()
12899

129-
static NSCache *RCTGetCache()
130-
{
131-
// We want all instances to share the same cache since they will be reading/writing the same files.
132-
static NSCache *cache;
133-
static dispatch_once_t onceToken;
134-
dispatch_once(&onceToken, ^{
135-
cache = [NSCache new];
136-
cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
137-
138-
// Clear cache in the event of a memory warning
139-
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
140-
[cache removeAllObjects];
141-
}];
142-
});
143-
return cache;
144-
}
100+
#define RCTGetStorageDirectory() _storageDirectory
101+
#define RCTGetManifestFilePath() _manifestFilePath
102+
#define RCTGetMethodQueue() self.methodQueue
103+
#define RCTGetCache() self.cache
145104

146-
static BOOL RCTHasCreatedStorageDirectory = NO;
147-
static NSDictionary *RCTDeleteStorageDirectory()
105+
static NSDictionary *RCTDeleteStorageDirectory(NSString *storageDirectory)
148106
{
149107
NSError *error;
150-
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error];
151-
RCTHasCreatedStorageDirectory = NO;
108+
[[NSFileManager defaultManager] removeItemAtPath:storageDirectory error:&error];
152109
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil;
153110
}
111+
#define RCTDeleteStorageDirectory() RCTDeleteStorageDirectory(_storageDirectory)
154112

155113
#pragma mark - RCTAsyncLocalStorage
156114

115+
@interface RCTAsyncLocalStorage ()
116+
117+
@property (nonatomic, copy) NSString *storageDirectory;
118+
@property (nonatomic, copy) NSString *manifestFilePath;
119+
120+
@end
121+
157122
@implementation RCTAsyncLocalStorage
158123
{
159124
BOOL _haveSetup;
160125
// The manifest is a dictionary of all keys with small values inlined. Null values indicate values that are stored
161126
// in separate files (as opposed to nil values which don't exist). The manifest is read off disk at startup, and
162127
// written to disk after all mutations.
163128
NSMutableDictionary<NSString *, NSString *> *_manifest;
129+
NSCache *_cache;
130+
dispatch_once_t *_cacheOnceToken;
164131
}
165132

166-
RCT_EXPORT_MODULE()
167-
168-
- (dispatch_queue_t)methodQueue
133+
// NOTE(nikki93): Prevents the module from being auto-initialized and allows us to pass our own `storageDirectory`
134+
+ (NSString *)moduleName { return @"RCTAsyncLocalStorage"; }
135+
- (instancetype)initWithStorageDirectory:(NSString *)storageDirectory
169136
{
170-
return RCTGetMethodQueue();
137+
if ((self = [super init])) {
138+
_storageDirectory = storageDirectory;
139+
_manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName];
140+
}
141+
return self;
171142
}
172143

173-
- (void)clearAllData
144+
// NOTE(nikki93): Use the default `methodQueue` since instances have different storage directories
145+
@synthesize methodQueue = _methodQueue;
146+
147+
- (NSCache *)cache
174148
{
175-
dispatch_async(RCTGetMethodQueue(), ^{
176-
[self->_manifest removeAllObjects];
177-
[RCTGetCache() removeAllObjects];
178-
RCTDeleteStorageDirectory();
149+
dispatch_once(&_cacheOnceToken, ^{
150+
_cache = [NSCache new];
151+
_cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
152+
153+
// Clear cache in the event of a memory warning
154+
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
155+
[_cache removeAllObjects];
156+
}];
179157
});
158+
return _cache;
180159
}
181160

182-
+ (void)clearAllData
161+
- (void)clearAllData
183162
{
184163
dispatch_async(RCTGetMethodQueue(), ^{
164+
[self->_manifest removeAllObjects];
185165
[RCTGetCache() removeAllObjects];
186166
RCTDeleteStorageDirectory();
187167
});
@@ -223,15 +203,13 @@ - (NSDictionary *)_ensureSetup
223203
#endif
224204

225205
NSError *error = nil;
226-
if (!RCTHasCreatedStorageDirectory) {
227-
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory()
228-
withIntermediateDirectories:YES
229-
attributes:nil
230-
error:&error];
231-
if (error) {
232-
return RCTMakeError(@"Failed to create storage directory.", error, nil);
233-
}
234-
RCTHasCreatedStorageDirectory = YES;
206+
// NOTE(nikki93): `withIntermediateDirectories:YES` makes this idempotent
207+
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory()
208+
withIntermediateDirectories:YES
209+
attributes:nil
210+
error:&error];
211+
if (error) {
212+
return RCTMakeError(@"Failed to create storage directory.", error, nil);
235213
}
236214
if (!_haveSetup) {
237215
NSDictionary *errorOut;

0 commit comments

Comments
 (0)