-
Notifications
You must be signed in to change notification settings - Fork 475
Added the ability to set the storage location on iOS #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
81346a8
20d18e6
3f4f92a
75823da
5041f58
7d96ab7
3b63039
3d0c10c
17489ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,25 @@ | |
#import "RCTLog.h" | ||
#import "RCTUtils.h" | ||
|
||
typedef NS_ENUM(NSInteger, RCTStorageLocation) { | ||
Documents, | ||
ApplicationSupport | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @krizzu Is this what we want? only support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's been discussed in #13, where Do you have any suggestion if there should be more/less options to choose from? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @krizzu Oh, I scan those things, so now I think we may need to handle two things:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}; | ||
|
||
@implementation RCTConvert (RCTStorageLocation) | ||
|
||
RCT_ENUM_CONVERTER(RCTStorageLocation, (@{ | ||
@"documents": @(Documents), | ||
@"applicationSupport": @(ApplicationSupport), | ||
}), Documents, integerValue) | ||
|
||
@end | ||
|
||
static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1"; | ||
static NSString *const RCTManifestFileName = @"manifest.json"; | ||
static const NSUInteger RCTInlineValueThreshold = 1024; | ||
static NSString *manifestFilePath = nil; | ||
static NSString *storageDirectory = nil; | ||
|
||
#pragma mark - Static helper functions | ||
|
||
|
@@ -66,29 +82,33 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> * | |
return nil; | ||
} | ||
|
||
static NSString *RCTGetStorageDirectory() | ||
static NSString *RCTGetStorageDirectory(RCTStorageLocation storageLocation) | ||
{ | ||
static NSString *storageDirectory = nil; | ||
if (storageDirectory != nil) { | ||
return storageDirectory; | ||
} | ||
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; | ||
NSSearchPathDirectory path = NSDocumentDirectory; | ||
if (storageLocation == ApplicationSupport) { | ||
path = NSApplicationSupportDirectory; | ||
} | ||
storageDirectory = NSSearchPathForDirectoriesInDomains(path, NSUserDomainMask, YES).firstObject; | ||
#endif | ||
storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory]; | ||
}); | ||
return storageDirectory; | ||
} | ||
|
||
static NSString *RCTGetManifestFilePath() | ||
static NSString *RCTGetManifestFilePath(RCTStorageLocation storageLocation) | ||
{ | ||
static NSString *manifestFilePath = nil; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName]; | ||
}); | ||
return manifestFilePath; | ||
if (manifestFilePath != nil) { | ||
return manifestFilePath; | ||
} | ||
return manifestFilePath = [RCTGetStorageDirectory(storageLocation) stringByAppendingPathComponent:RCTManifestFileName];; | ||
} | ||
|
||
// Only merges objects - all other types are just clobbered (including arrays) | ||
|
@@ -148,14 +168,19 @@ static dispatch_queue_t RCTGetMethodQueue() | |
} | ||
|
||
static BOOL RCTHasCreatedStorageDirectory = NO; | ||
static NSDictionary *RCTDeleteStorageDirectory() | ||
static NSDictionary *RCTDeleteStorageDirectory(RCTStorageLocation storageLocation) | ||
{ | ||
NSError *error; | ||
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]; | ||
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory(storageLocation) error:&error]; | ||
RCTHasCreatedStorageDirectory = NO; | ||
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil; | ||
} | ||
|
||
static BOOL RCTStorageDirectoryExists(RCTStorageLocation storageLocation) | ||
{ | ||
return [[NSFileManager defaultManager] fileExistsAtPath:RCTGetStorageDirectory(storageLocation)]; | ||
} | ||
|
||
#pragma mark - RCTAsyncLocalStorage | ||
|
||
@implementation RCTAsyncLocalStorage | ||
|
@@ -165,6 +190,7 @@ @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<NSString *, NSString *> *_manifest; | ||
RCTStorageLocation storageLocation; | ||
} | ||
|
||
RCT_EXPORT_MODULE() | ||
|
@@ -179,25 +205,33 @@ - (void)clearAllData | |
dispatch_async(RCTGetMethodQueue(), ^{ | ||
[self->_manifest removeAllObjects]; | ||
[RCTGetCache() removeAllObjects]; | ||
RCTDeleteStorageDirectory(); | ||
RCTDeleteStorageDirectory(self->storageLocation); | ||
}); | ||
} | ||
|
||
+ (void)clearAllData | ||
{ | ||
dispatch_async(RCTGetMethodQueue(), ^{ | ||
[RCTGetCache() removeAllObjects]; | ||
RCTDeleteStorageDirectory(); | ||
if (RCTStorageDirectoryExists(Documents)) { | ||
RCTDeleteStorageDirectory(Documents); | ||
} | ||
if (RCTStorageDirectoryExists(ApplicationSupport)) { | ||
RCTDeleteStorageDirectory(ApplicationSupport); | ||
} | ||
}); | ||
} | ||
|
||
- (void)invalidate | ||
{ | ||
if (_clearOnInvalidate) { | ||
[RCTGetCache() removeAllObjects]; | ||
RCTDeleteStorageDirectory(); | ||
RCTDeleteStorageDirectory(storageLocation); | ||
} | ||
_clearOnInvalidate = NO; | ||
manifestFilePath = nil; | ||
storageDirectory = nil; | ||
RCTHasCreatedStorageDirectory = false; | ||
[_manifest removeAllObjects]; | ||
_haveSetup = NO; | ||
} | ||
|
@@ -215,7 +249,7 @@ - (void)dealloc | |
- (NSString *)_filePathForKey:(NSString *)key | ||
{ | ||
NSString *safeFileName = RCTMD5Hash(key); | ||
return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName]; | ||
return [RCTGetStorageDirectory(storageLocation) stringByAppendingPathComponent:safeFileName]; | ||
} | ||
|
||
- (NSDictionary *)_ensureSetup | ||
|
@@ -228,7 +262,7 @@ - (NSDictionary *)_ensureSetup | |
|
||
NSError *error = nil; | ||
if (!RCTHasCreatedStorageDirectory) { | ||
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory() | ||
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory(storageLocation) | ||
withIntermediateDirectories:YES | ||
attributes:nil | ||
error:&error]; | ||
|
@@ -239,7 +273,7 @@ - (NSDictionary *)_ensureSetup | |
} | ||
if (!_haveSetup) { | ||
NSDictionary *errorOut; | ||
NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), RCTManifestFileName, &errorOut); | ||
NSString *serialized = RCTReadFile(RCTGetManifestFilePath(storageLocation), RCTManifestFileName, &errorOut); | ||
_manifest = serialized ? RCTJSONParseMutable(serialized, &error) : [NSMutableDictionary new]; | ||
if (error) { | ||
RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error); | ||
|
@@ -254,7 +288,7 @@ - (NSDictionary *)_writeManifest:(NSMutableArray<NSDictionary *> **)errors | |
{ | ||
NSError *error; | ||
NSString *serialized = RCTJSONStringify(_manifest, &error); | ||
[serialized writeToFile:RCTGetManifestFilePath() atomically:YES encoding:NSUTF8StringEncoding error:&error]; | ||
[serialized writeToFile:RCTGetManifestFilePath(storageLocation) atomically:YES encoding:NSUTF8StringEncoding error:&error]; | ||
NSDictionary *errorOut; | ||
if (error) { | ||
errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil); | ||
|
@@ -441,7 +475,7 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL | |
{ | ||
[_manifest removeAllObjects]; | ||
[RCTGetCache() removeAllObjects]; | ||
NSDictionary *error = RCTDeleteStorageDirectory(); | ||
NSDictionary *error = RCTDeleteStorageDirectory(storageLocation); | ||
callback(@[RCTNullIfNil(error)]); | ||
} | ||
|
||
|
@@ -455,4 +489,10 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL | |
} | ||
} | ||
|
||
RCT_EXPORT_METHOD(setStorageLocation:(RCTStorageLocation)location) | ||
{ | ||
storageLocation = location; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can have equal check in here. |
||
[self invalidate]; | ||
} | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -342,6 +342,24 @@ const AsyncStorage = { | |
}); | ||
}); | ||
}, | ||
|
||
/** | ||
* Call this set the storage location. | ||
* @param storageLocation The storage location. Can be 'documents' or 'applicationSupport'. | ||
* | ||
* **Note**: changing this only sets the location for future actions and does not migrate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit is bit worrying. We had similar issue #55, where storage location was changed and devs were losing data. Maybe we could come up with migration plan for data? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default locations is still the same, imo devs can only loose data once updated to a new version which contains this change AND change the storage location specifically without copying their data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks. We should provide a note in docs that this can happen, so they know upfront. |
||
* the store from the previous location. | ||
* | ||
* @platform ios | ||
*/ | ||
setStorageLocationIOS: function(storageLocation: string) { | ||
RCTAsyncStorage.setStorageLocation(storageLocation); | ||
}, | ||
|
||
StorageLocationIOS: { | ||
documents: 'documents', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could export constants to be used in JS, what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yeah that's a great shout! will do that |
||
applicationSupport: 'applicationSupport', | ||
}, | ||
}; | ||
|
||
// Not all native implementations support merge. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we conform Apple naming style?
