@@ -79,13 +79,24 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> *
7979 return nil ;
8080}
8181
82+ // DO NOT USE
83+ // This is used internally to migrate data from the old file location to the new one.
84+ // Please use `RCTCreateStorageDirectoryPath` instead
85+ static NSString *RCTCreateStorageDirectoryPath_deprecated (NSString *storageDir) {
86+ NSString *storageDirectoryPath;
87+ #if TARGET_OS_TV
88+ storageDirectoryPath = NSSearchPathForDirectoriesInDomains (NSCachesDirectory, NSUserDomainMask, YES ).firstObject ;
89+ #else
90+ storageDirectoryPath = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES ).firstObject ;
91+ #endif
92+ storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent: storageDir];
93+ return storageDirectoryPath;
94+ }
95+
8296static NSString *RCTCreateStorageDirectoryPath (NSString *storageDir) {
83- NSString *storageDirectoryPath;
84- #if TARGET_OS_TV
85- storageDirectoryPath = NSSearchPathForDirectoriesInDomains (NSCachesDirectory, NSUserDomainMask, YES ).firstObject ;
86- #else
87- storageDirectoryPath = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES ).firstObject ;
88- #endif
97+ // We should use the "Application Support/[bundleID]" folder for persistent data storage that's hidden from users
98+ NSString *storageDirectoryPath = NSSearchPathForDirectoriesInDomains (NSApplicationSupportDirectory, NSUserDomainMask, YES ).firstObject ;
99+ storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent: [[NSBundle mainBundle ] bundleIdentifier ]]; // Per Apple's docs, all app content in Application Support must be within a subdirectory of the app's bundle identifier
89100 storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent: storageDir];
90101 return storageDirectoryPath;
91102}
@@ -102,7 +113,7 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> *
102113
103114static NSString *RCTCreateManifestFilePath (NSString *storageDirectory)
104115{
105- return [RCTCreateStorageDirectoryPath ( storageDirectory) stringByAppendingPathComponent: RCTManifestFileName];
116+ return [storageDirectory stringByAppendingPathComponent: RCTManifestFileName];
106117}
107118
108119static NSString *RCTGetManifestFilePath ()
@@ -194,58 +205,61 @@ static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *erro
194205 RCTLogWarn (@" %@ : %@ " , reason, error ? error.description : @" " );
195206}
196207
197- static void RCTStorageDirectoryCleanupOld ()
208+ static void RCTStorageDirectoryCleanupOld (NSString *oldDirectoryPath )
198209{
199210 NSError *error;
200- if (![[NSFileManager defaultManager ] removeItemAtPath: RCTCreateStorageDirectoryPath (RCTOldStorageDirectory) error: &error]) {
211+ if (![[NSFileManager defaultManager ] removeItemAtPath: oldDirectoryPath error: &error]) {
201212 RCTStorageDirectoryMigrationLogError (@" Failed to remove old storage directory during migration" , error);
202213 }
203214}
204215
205- static void RCTStorageDirectoryMigrate ()
216+ static void RCTStorageDirectoryMigrate (NSString *oldDirectoryPath, NSString *newDirectoryPath, BOOL shouldCleanupOldDirectory )
206217{
207218 NSError *error;
208219 // Migrate data by copying old storage directory to new storage directory location
209- if (![[NSFileManager defaultManager ] copyItemAtPath: RCTCreateStorageDirectoryPath (RCTOldStorageDirectory) toPath: RCTGetStorageDirectory () error: &error]) {
220+ if (![[NSFileManager defaultManager ] copyItemAtPath: oldDirectoryPath toPath: newDirectoryPath error: &error]) {
210221 RCTStorageDirectoryMigrationLogError (@" Failed to copy old storage directory to new storage directory location during migration" , error);
211- } else {
222+ } else if (shouldCleanupOldDirectory) {
212223 // If copying succeeds, remove old storage directory
213- RCTStorageDirectoryCleanupOld ();
224+ RCTStorageDirectoryCleanupOld (oldDirectoryPath );
214225 }
215226}
216227
217228/* *
218229 * This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
219- * Data is migrated from the "RNCAsyncLocalStorage_V1" directory to the "RCTAsyncLocalStorage_V1" directory.
230+ * Check that data is migrated from the old location to the new location
231+ * fromStorageDirectory: the directory where the older data lives
232+ * toStorageDirectory: the directory where the new data should live
233+ * shouldCleanupOldDirectoryAndOverwriteNewDirectory: YES if we should delete the old directory's contents and overwrite the new directory's contents during the migration to the new directory
220234 */
221- static void RCTStorageDirectoryMigrationCheck ()
235+ static void RCTStorageDirectoryMigrationCheck (NSString *fromStorageDirectory, NSString *toStorageDirectory, BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory )
222236{
223- static dispatch_once_t onceToken;
224- dispatch_once (&onceToken, ^{
225- NSError *error;
226- BOOL isDir;
227- // If the old directory exists, it means we may need to migrate old data to the new directory
228- if ([[NSFileManager defaultManager ] fileExistsAtPath: RCTCreateStorageDirectoryPath (RCTOldStorageDirectory) isDirectory: &isDir] && isDir) {
229- // Check if the new storage directory location already exists
230- if ([[NSFileManager defaultManager ] fileExistsAtPath: RCTGetStorageDirectory ()]) {
231- // If new storage location exists, check if the new storage has been modified sooner
232- if ([RCTManifestModificationDate (RCTGetManifestFilePath ()) compare: RCTManifestModificationDate (RCTCreateManifestFilePath (RCTOldStorageDirectory))] == 1 ) {
233- // If new location has been modified more recently, simply clean out old data
234- RCTStorageDirectoryCleanupOld ();
237+ NSError *error;
238+ BOOL isDir;
239+ NSFileManager *fileManager = [NSFileManager defaultManager ];
240+ // If the old directory exists, it means we may need to migrate old data to the new directory
241+ if ([fileManager fileExistsAtPath: fromStorageDirectory isDirectory: &isDir] && isDir) {
242+ // Check if the new storage directory location already exists
243+ if ([fileManager fileExistsAtPath: toStorageDirectory]) {
244+ // If new storage location exists, check if the new storage has been modified sooner in which case we may want to cleanup the old location
245+ if ([RCTManifestModificationDate (RCTCreateManifestFilePath (toStorageDirectory)) compare: RCTManifestModificationDate (RCTCreateManifestFilePath (fromStorageDirectory))] == 1 ) {
246+ // If new location has been modified more recently, simply clean out old data
247+ if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
248+ RCTStorageDirectoryCleanupOld (fromStorageDirectory);
249+ }
250+ } else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
251+ // If old location has been modified more recently, remove new storage and migrate
252+ if (![fileManager removeItemAtPath: toStorageDirectory error: &error]) {
253+ RCTStorageDirectoryMigrationLogError (@" Failed to remove new storage directory during migration" , error);
235254 } else {
236- // If old location has been modified more recently, remove new storage and migrate
237- if (![[NSFileManager defaultManager ] removeItemAtPath: RCTGetStorageDirectory () error: &error]) {
238- RCTStorageDirectoryMigrationLogError (@" Failed to remove new storage directory during migration" , error);
239- } else {
240- RCTStorageDirectoryMigrate ();
241- }
255+ RCTStorageDirectoryMigrate (fromStorageDirectory, toStorageDirectory, shouldCleanupOldDirectoryAndOverwriteNewDirectory);
242256 }
243- } else {
244- // If new storage location doesn't exist, migrate data
245- RCTStorageDirectoryMigrate ();
246257 }
258+ } else {
259+ // If new storage location doesn't exist, migrate data
260+ RCTStorageDirectoryMigrate (fromStorageDirectory, toStorageDirectory, shouldCleanupOldDirectoryAndOverwriteNewDirectory);
247261 }
248- });
262+ }
249263}
250264
251265#pragma mark - RNCAsyncStorage
@@ -269,7 +283,13 @@ - (instancetype)init
269283 if (!(self = [super init ])) {
270284 return nil ;
271285 }
272- RCTStorageDirectoryMigrationCheck ();
286+
287+ // First migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" to "Documents/.../RCTAsyncLocalStorage_V1"
288+ RCTStorageDirectoryMigrationCheck (RCTCreateStorageDirectoryPath_deprecated (RCTOldStorageDirectory), RCTCreateStorageDirectoryPath_deprecated (RCTStorageDirectory), YES );
289+
290+ // Then migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to "Application Support/[bundleID]/RCTAsyncLocalStorage_V1"
291+ RCTStorageDirectoryMigrationCheck (RCTCreateStorageDirectoryPath_deprecated (RCTStorageDirectory), RCTCreateStorageDirectoryPath (RCTStorageDirectory), NO );
292+
273293 return self;
274294}
275295
@@ -346,7 +366,7 @@ - (NSDictionary *)_ensureSetup
346366
347367 if (!_haveSetup) {
348368 NSDictionary *errorOut = nil ;
349- NSString *serialized = RCTReadFile (RCTGetManifestFilePath (), RCTManifestFileName, &errorOut);
369+ NSString *serialized = RCTReadFile (RCTCreateStorageDirectoryPath ( RCTGetManifestFilePath () ), RCTManifestFileName, &errorOut);
350370 if (!serialized) {
351371 if (errorOut) {
352372 // We cannot simply create a new manifest in case the file does exist but we have no access to it.
@@ -377,7 +397,7 @@ - (NSDictionary *)_writeManifest:(NSMutableArray<NSDictionary *> **)errors
377397{
378398 NSError *error;
379399 NSString *serialized = RCTJSONStringify (_manifest, &error);
380- [serialized writeToFile: RCTGetManifestFilePath () atomically: YES encoding: NSUTF8StringEncoding error: &error];
400+ [serialized writeToFile: RCTCreateStorageDirectoryPath ( RCTGetManifestFilePath () ) atomically: YES encoding: NSUTF8StringEncoding error: &error];
381401 NSDictionary *errorOut;
382402 if (error) {
383403 errorOut = RCTMakeError (@" Failed to write manifest file." , error, nil );
0 commit comments