Skip to content

iOS clean migration from RNCAsyncStorage_V1 to RTCAsyncStorage_V1 #64

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

Merged
merged 12 commits into from
Apr 11, 2019
108 changes: 101 additions & 7 deletions ios/RNCAsyncStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import <React/RCTUtils.h>

static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1";
static NSString *const RCTOldStorageDirectory = @"RNCAsyncLocalStorage_V1";
static NSString *const RCTManifestFileName = @"manifest.json";
static const NSUInteger RCTInlineValueThreshold = 1024;

Expand Down Expand Up @@ -78,27 +79,38 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> *
return nil;
}

static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir) {
NSString *storageDirectoryPath;
#if TARGET_OS_TV
storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
#else
storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
#endif
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
return storageDirectoryPath;
}

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];
storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory);
});
return storageDirectory;
}

static NSString *RCTCreateManifestFilePath(NSString *storageDirectory)
{
return [RCTCreateStorageDirectoryPath(storageDirectory) stringByAppendingPathComponent:RCTManifestFileName];
}

static NSString *RCTGetManifestFilePath()
{
static NSString *manifestFilePath = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manifestFilePath = [RCTGetStorageDirectory() stringByAppendingPathComponent:RCTManifestFileName];
manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory);
});
return manifestFilePath;
}
Expand Down Expand Up @@ -168,6 +180,74 @@ static dispatch_queue_t RCTGetMethodQueue()
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil;
}

static NSDate *RCTManifestModificationDate(NSString *manifestFilePath)
{
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:manifestFilePath error:nil];
return [attributes fileModificationDate];
}

/**
* Creates an NSException used during Storage Directory Migration.
*/
static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *error)
{
RCTLogWarn(@"%@: %@", reason, error ? error.description : @"");
}

static void RCTStorageDirectoryCleanupOld()
{
NSError *error;
if (![[NSFileManager defaultManager] removeItemAtPath:RCTCreateStorageDirectoryPath(RCTOldStorageDirectory) error:&error]) {
RCTStorageDirectoryMigrationLogError(@"Failed to remove old storage directory during migration", error);
}
}

static void RCTStorageDirectoryMigrate()
{
NSError *error;
// Migrate data by copying old storage directory to new storage directory location
if (![[NSFileManager defaultManager] copyItemAtPath:RCTCreateStorageDirectoryPath(RCTOldStorageDirectory) toPath:RCTGetStorageDirectory() error:&error]) {
RCTStorageDirectoryMigrationLogError(@"Failed to copy old storage directory to new storage directory location during migration", error);
} else {
// If copying succeeds, remove old storage directory
RCTStorageDirectoryCleanupOld();
}
}

/**
* This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
* Data is migrated from the "RNCAsyncLocalStorage_V1" directory to the "RCTAsyncLocalStorage_V1" directory.
*/
static void RCTStorageDirectoryMigrationCheck()
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSError *error;
BOOL isDir;
// If the old directory exists, it means we may need to migrate old data to the new directory
if ([[NSFileManager defaultManager] fileExistsAtPath:RCTCreateStorageDirectoryPath(RCTOldStorageDirectory) isDirectory:&isDir] && isDir) {
// Check if the new storage directory location already exists
if ([[NSFileManager defaultManager] fileExistsAtPath:RCTGetStorageDirectory()]) {
// If new storage location exists, check if the new storage has been modified sooner
if ([RCTManifestModificationDate(RCTGetManifestFilePath()) compare:RCTManifestModificationDate(RCTCreateManifestFilePath(RCTOldStorageDirectory))] == 1) {
// If new location has been modified more recently, simply clean out old data
RCTStorageDirectoryCleanupOld();
} else {
// If old location has been modified more recently, remove new storage and migrate
if (![[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]) {
RCTStorageDirectoryMigrationLogError(@"Failed to remove new storage directory during migration", error);
} else {
RCTStorageDirectoryMigrate();
}
}
} else {
// If new storage location doesn't exist, migrate data
RCTStorageDirectoryMigrate();
}
}
});
}

#pragma mark - RNCAsyncStorage

@implementation RNCAsyncStorage
Expand All @@ -179,6 +259,20 @@ @implementation RNCAsyncStorage
NSMutableDictionary<NSString *, NSString *> *_manifest;
}

+ (BOOL)requiresMainQueueSetup
{
return NO;
}

- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
RCTStorageDirectoryMigrationCheck();
return self;
}

RCT_EXPORT_MODULE()

- (dispatch_queue_t)methodQueue
Expand Down