From b114d4e20d45373ec2752ad1303addc753e25fd1 Mon Sep 17 00:00:00 2001 From: Aleh Dzenisiuk Date: Fri, 27 Dec 2019 10:55:25 +0100 Subject: [PATCH 1/3] fix: Don't reset the storage when the manifest file cannot be read because of iOS data protection This is to distinguish "manifest file does not exist or is corrupted" and "have no access to the manifest file yet" cases. Starting with an empty dictionary is a reasonable fallback in case the manifest does not exist or cannot be parsed because of being corrupted or incompatible. However doing so might lead to unnecessary data loss when the app simply has no access to its documents yet due to iOS data protection (if enabled and when the app launches while the device is locked due to e.g. geofencing, silent notification, external accessory, etc). --- ios/RNCAsyncStorage.m | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.m index 18556296..c05b8164 100644 --- a/ios/RNCAsyncStorage.m +++ b/ios/RNCAsyncStorage.m @@ -343,16 +343,33 @@ - (NSDictionary *)_ensureSetup } RCTHasCreatedStorageDirectory = YES; } + if (!_haveSetup) { - NSDictionary *errorOut; + NSDictionary *errorOut = nil; NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), RCTManifestFileName, &errorOut); - _manifest = serialized ? RCTJSONParseMutable(serialized, &error) : [NSMutableDictionary new]; - if (error) { - RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error); - _manifest = [NSMutableDictionary new]; + if (!serialized) { + if (errorOut) { + // We cannot simply create a new manifest in case the file does exist but we have no access to it. + // This can happen when data protection is enabled for the app and we are trying to read the manifect + // while the device is locked. (The app can be started by the system even if the device is locked due to + // e.g. a geofence event.) + RCTLogError(@"Could not open the existing manifest, perhaps data protection is enabled?\n\n%@", errorOut); + return errorOut; + } else { + // We can get nil without errors only when the file does not exist. + RCTLogTrace(@"Manifest does not exist - creating a new one.\n\n%@", errorOut); + _manifest = [NSMutableDictionary new]; + } + } else { + _manifest = RCTJSONParseMutable(serialized, &error); + if (!_manifest) { + RCTLogError(@"Failed to parse manifest - creating a new one.\n\n%@", error); + _manifest = [NSMutableDictionary new]; + } } _haveSetup = YES; } + return nil; } From b71f5eef0b8a8ea568f8229b5958267c1b8a2b14 Mon Sep 17 00:00:00 2001 From: Aleh Dzenisiuk Date: Fri, 27 Dec 2019 14:02:46 +0100 Subject: [PATCH 2/3] fixup! fix: Don't reset the storage when the manifest file cannot be read because of iOS data protection --- ios/RNCAsyncStorage.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.m index c05b8164..7466e346 100644 --- a/ios/RNCAsyncStorage.m +++ b/ios/RNCAsyncStorage.m @@ -348,20 +348,20 @@ - (NSDictionary *)_ensureSetup NSDictionary *errorOut = nil; NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), RCTManifestFileName, &errorOut); if (!serialized) { - if (errorOut) { - // We cannot simply create a new manifest in case the file does exist but we have no access to it. - // This can happen when data protection is enabled for the app and we are trying to read the manifect - // while the device is locked. (The app can be started by the system even if the device is locked due to - // e.g. a geofence event.) - RCTLogError(@"Could not open the existing manifest, perhaps data protection is enabled?\n\n%@", errorOut); - return errorOut; + if (errorOut) { + // We cannot simply create a new manifest in case the file does exist but we have no access to it. + // This can happen when data protection is enabled for the app and we are trying to read the manifect + // while the device is locked. (The app can be started by the system even if the device is locked due to + // e.g. a geofence event.) + RCTLogError(@"Could not open the existing manifest, perhaps data protection is enabled?\n\n%@", errorOut); + return errorOut; } else { - // We can get nil without errors only when the file does not exist. + // We can get nil without errors only when the file does not exist. RCTLogTrace(@"Manifest does not exist - creating a new one.\n\n%@", errorOut); _manifest = [NSMutableDictionary new]; } } else { - _manifest = RCTJSONParseMutable(serialized, &error); + _manifest = RCTJSONParseMutable(serialized, &error); if (!_manifest) { RCTLogError(@"Failed to parse manifest - creating a new one.\n\n%@", error); _manifest = [NSMutableDictionary new]; From ff7c3f08f8dcc8825aacb04e5f092c1ec44d3048 Mon Sep 17 00:00:00 2001 From: Aleh Dzenisiuk Date: Wed, 8 Jan 2020 15:52:50 +0100 Subject: [PATCH 3/3] Update ios/RNCAsyncStorage.m Co-Authored-By: Krzysztof Borowy --- ios/RNCAsyncStorage.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.m index 7466e346..bd9926fa 100644 --- a/ios/RNCAsyncStorage.m +++ b/ios/RNCAsyncStorage.m @@ -350,7 +350,7 @@ - (NSDictionary *)_ensureSetup if (!serialized) { if (errorOut) { // We cannot simply create a new manifest in case the file does exist but we have no access to it. - // This can happen when data protection is enabled for the app and we are trying to read the manifect + // This can happen when data protection is enabled for the app and we are trying to read the manifest // while the device is locked. (The app can be started by the system even if the device is locked due to // e.g. a geofence event.) RCTLogError(@"Could not open the existing manifest, perhaps data protection is enabled?\n\n%@", errorOut);