From fd9d5f69331606b91d44590abaaed164a6b0f48a Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Fri, 6 Nov 2020 23:05:55 +0100 Subject: [PATCH] chore: Run clang-format on everything --- .gitignore | 3 +- .npmignore | 1 + .vscode/extensions.json | 9 + CONTRIBUTING.md | 9 +- example/App.js | 2 +- .../AsyncStorageDevSupport.m | 6 +- .../AsyncStorageExample_macOSUITests.m | 240 ++-- example/windows/AsyncStorageExample/App.cpp | 7 +- example/windows/AsyncStorageExample/App.h | 7 +- .../ReactPackageProvider.cpp | 12 +- .../ReactPackageProvider.h | 9 +- example/windows/AsyncStorageExample/pch.h | 14 +- ios/RNCAsyncStorage.h | 11 +- ios/RNCAsyncStorage.m | 1109 +++++++++-------- ios/RNCAsyncStorageDelegate.h | 5 +- metro.config.windows.js | 4 +- package.json | 2 + windows/ReactNativeAsyncStorage/DBStorage.cpp | 270 ++-- windows/ReactNativeAsyncStorage/DBStorage.h | 67 +- .../ReactNativeAsyncStorage/RNCAsyncStorage.h | 174 +-- .../ReactPackageProvider.cpp | 12 +- .../ReactPackageProvider.h | 21 +- windows/ReactNativeAsyncStorage/pch.h | 7 +- 23 files changed, 1109 insertions(+), 892 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.gitignore b/.gitignore index 4d527c0a..c027235d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,8 @@ buck-out/ *.keystore # Editor config -.vscode +.vscode/* +!.vscode/extensions.json .expo /web-build diff --git a/.npmignore b/.npmignore index 163fc8d7..0f8d8c1b 100644 --- a/.npmignore +++ b/.npmignore @@ -8,6 +8,7 @@ README.md # Config files babel.config.js +.clang-format .eslintrc .flowconfig .watchmanconfig diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..02aba408 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "ms-vscode.cpptools" + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1328bba1..42fab25a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,19 +19,24 @@ When you're sending a pull request: * Communication is a key. If you want fix/add something, please open new/find existing issue, so we can discuss it. * We prefer small pull requests focused on one change, as those are easier to test/check. * Please make sure that all tests are passing on your local machine. +* Please make sure you've run formatters and linters locally. + * In VS Code, you can press ⇧+Alt+F or ⇧⌥F to format the current file. + * To format C++ and Objective-C files, make sure you have the [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) installed. + * To format JavaScript files, please install [Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). + * From the command line, you can run `yarn format:c` and `yarn format:js` to format C-based languages and JavaScript respectively. The first command requires that you've already installed [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html). * Follow the template when opening a PR. ## Commits and versioning All PRs are squashed into `master` branch and wrapped up in a single commit, following [conventional commit message](https://www.conventionalcommits.org/en/v1.0.0-beta.3). Combined with [semantic versioning](https://semver.org/), this allows us to have a frequent releases of the library. -*Note*: We don't force this convention on Pull Requests from contributors, but it's a clean way to see what type of changes are made, so feel free to follow it. +*Note*: We don't force this convention on Pull Requests from contributors, but it's a clean way to see what type of changes are made, so feel free to follow it. Most notably prefixes you'll see: * **fix**: Bug fixes, triggers *patch* release -* **feat**: New feature implemented, triggers *minor* +* **feat**: New feature implemented, triggers *minor* * **chore**: Changes that are not affecting end user (CI config changes, scripts, ["grunt work"](https://stackoverflow.com/a/26944812/3510245)) * **docs**: Documentation changes. * **perf**: A code change that improves performance. diff --git a/example/App.js b/example/App.js index a4464835..d3584d83 100644 --- a/example/App.js +++ b/example/App.js @@ -54,7 +54,7 @@ export default class App extends Component { this.setState({restarting: true}, () => this.setState({restarting: false})); }; - _changeTest = testName => { + _changeTest = (testName) => { this.setState({currentTest: TESTS[testName]}); }; diff --git a/example/ios/AsyncStorageExample/AsyncStorageDevSupport.m b/example/ios/AsyncStorageExample/AsyncStorageDevSupport.m index aaa9754c..ec49e844 100644 --- a/example/ios/AsyncStorageExample/AsyncStorageDevSupport.m +++ b/example/ios/AsyncStorageExample/AsyncStorageDevSupport.m @@ -11,7 +11,7 @@ return [bridge moduleForClass:[RNCAsyncStorage class]]; } -@interface AsyncStorageTestSupport : NSObject +@interface AsyncStorageTestSupport : NSObject @end @implementation AsyncStorageDevSupport { @@ -139,14 +139,18 @@ @implementation AsyncStorageTestSupport RCT_EXPORT_MODULE() +// clang-format off RCT_EXPORT_METHOD(test_setDelegate:(RCTResponseSenderBlock)callback) +// clang-format on { RNCAsyncStorage *asyncStorage = RNCAsyncStorageGetInstance(_bridge); asyncStorage.delegate = _sharedInstance; callback(@[]); } +// clang-format off RCT_EXPORT_METHOD(test_unsetDelegate:(RCTResponseSenderBlock)callback) +// clang-format on { RNCAsyncStorage *asyncStorage = RNCAsyncStorageGetInstance(_bridge); asyncStorage.delegate = nil; diff --git a/example/macos/AsyncStorageExample-macOSUITests/AsyncStorageExample_macOSUITests.m b/example/macos/AsyncStorageExample-macOSUITests/AsyncStorageExample_macOSUITests.m index 3d6e3d1f..e193cc3f 100644 --- a/example/macos/AsyncStorageExample-macOSUITests/AsyncStorageExample_macOSUITests.m +++ b/example/macos/AsyncStorageExample-macOSUITests/AsyncStorageExample_macOSUITests.m @@ -5,155 +5,165 @@ * LICENSE file in the root directory of this source tree. */ -#import #import +#import @interface AsyncStorageExample_macOSUITests : XCTestCase @end -@implementation AsyncStorageExample_macOSUITests -{ - XCUIApplication *_app; - XCUIElement *_window; - XCUIElement *_restartButton; - XCUIElement *_getSetClearButton; - XCUIElement *_mergeItemButton; +@implementation AsyncStorageExample_macOSUITests { + XCUIApplication *_app; + XCUIElement *_window; + XCUIElement *_restartButton; + XCUIElement *_getSetClearButton; + XCUIElement *_mergeItemButton; } -- (void)setUp { - // In UI tests it is usually best to stop immediately when a failure occurs. - self.continueAfterFailure = NO; +- (void)setUp +{ + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; - _app = [[XCUIApplication alloc] init]; - [_app launch]; - - _window = _app.windows[@"RNCAsyncStorageExample macOS"]; - XCTAssert(_window.exists); + _app = [[XCUIApplication alloc] init]; + [_app launch]; - _restartButton = _window.otherElements[@"restart_button"].staticTexts.firstMatch; - XCTAssert(_restartButton.exists); + _window = _app.windows[@"RNCAsyncStorageExample macOS"]; + XCTAssert(_window.exists); - _getSetClearButton = _window.buttons[@"testType_getSetClear"].staticTexts.firstMatch; - XCTAssert(_getSetClearButton.exists); + _restartButton = _window.otherElements[@"restart_button"].staticTexts.firstMatch; + XCTAssert(_restartButton.exists); - _mergeItemButton = _window.buttons[@"testType_mergeItem"].staticTexts.firstMatch; - XCTAssert(_mergeItemButton.exists); - - [self sendTestAppCommand:@"rnc-asyncstorage://clear-all-storage"]; + _getSetClearButton = _window.buttons[@"testType_getSetClear"].staticTexts.firstMatch; + XCTAssert(_getSetClearButton.exists); + + _mergeItemButton = _window.buttons[@"testType_mergeItem"].staticTexts.firstMatch; + XCTAssert(_mergeItemButton.exists); + + [self sendTestAppCommand:@"rnc-asyncstorage://clear-all-storage"]; } -- (void)testShouldStoreValueInAsyncStorage { - [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; +- (void)testShouldStoreValueInAsyncStorage +{ + [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; + + [_getSetClearButton click]; - [_getSetClearButton click]; + XCUIElement *storedNumber = _window.staticTexts[@"storedNumber_text"]; - XCUIElement *storedNumber = _window.staticTexts[@"storedNumber_text"]; + XCTAssertEqualObjects(storedNumber.label, @""); - XCTAssertEqualObjects(storedNumber.label, @""); + XCUIElement *increaseBy10Button = + _window.buttons[@"increaseByTen_button"].staticTexts.firstMatch; - XCUIElement *increaseBy10Button = _window.buttons[@"increaseByTen_button"].staticTexts.firstMatch; + int tapTimes = arc4random_uniform(10) + 1; - int tapTimes = arc4random_uniform(10) + 1; - - for (int i = 0; i < tapTimes; i++) { - [increaseBy10Button click]; - } + for (int i = 0; i < tapTimes; i++) { + [increaseBy10Button click]; + } - NSString *expectedText = [NSString stringWithFormat:@"%d", tapTimes * 10]; - - XCTAssertEqualObjects(storedNumber.label, expectedText); + NSString *expectedText = [NSString stringWithFormat:@"%d", tapTimes * 10]; + + XCTAssertEqualObjects(storedNumber.label, expectedText); } -- (void)testShouldClearItem { - [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; +- (void)testShouldClearItem +{ + [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; - [_getSetClearButton click]; + [_getSetClearButton click]; - XCUIElement *increaseBy10Button = _window.buttons[@"increaseByTen_button"].staticTexts.firstMatch; - [increaseBy10Button click]; + XCUIElement *increaseBy10Button = + _window.buttons[@"increaseByTen_button"].staticTexts.firstMatch; + [increaseBy10Button click]; - XCUIElement *clearButton = _window.buttons[@"clear_button"].staticTexts.firstMatch; - [clearButton click]; - [_restartButton click]; + XCUIElement *clearButton = _window.buttons[@"clear_button"].staticTexts.firstMatch; + [clearButton click]; + [_restartButton click]; - XCUIElement *storedNumber = _window.staticTexts[@"storedNumber_text"]; - XCTAssertEqualObjects(storedNumber.label, @""); + XCUIElement *storedNumber = _window.staticTexts[@"storedNumber_text"]; + XCTAssertEqualObjects(storedNumber.label, @""); } -- (NSString *)performInputWithFormat:format { - NSString *name = arc4random_uniform(2) == 0 ? @"Jerry" : @"Sarah"; - NSString *age = arc4random_uniform(2) == 0 ? @"21" : @"23"; - NSString *eyeColor = arc4random_uniform(2) == 0 ? @"blue" : @"green"; - NSString *shoeSize = arc4random_uniform(2) == 0 ? @"9" : @"10"; - - XCUIElement *nameInput = _window.textFields[@"testInput-name"]; - [nameInput click]; - [nameInput typeText:name]; - - XCUIElement *ageInput = _window.textFields[@"testInput-age"]; - [ageInput click]; - [ageInput typeText:age]; - - XCUIElement *eyesInput = _window.textFields[@"testInput-eyes"]; - [eyesInput click]; - [eyesInput typeText:eyeColor]; - - XCUIElement *showInput = _window.textFields[@"testInput-shoe"]; - [showInput click]; - [showInput typeText:shoeSize]; - - return [NSString stringWithFormat:format, name, age, eyeColor, shoeSize]; +- (NSString *)performInputWithFormat:format +{ + NSString *name = arc4random_uniform(2) == 0 ? @"Jerry" : @"Sarah"; + NSString *age = arc4random_uniform(2) == 0 ? @"21" : @"23"; + NSString *eyeColor = arc4random_uniform(2) == 0 ? @"blue" : @"green"; + NSString *shoeSize = arc4random_uniform(2) == 0 ? @"9" : @"10"; + + XCUIElement *nameInput = _window.textFields[@"testInput-name"]; + [nameInput click]; + [nameInput typeText:name]; + + XCUIElement *ageInput = _window.textFields[@"testInput-age"]; + [ageInput click]; + [ageInput typeText:age]; + + XCUIElement *eyesInput = _window.textFields[@"testInput-eyes"]; + [eyesInput click]; + [eyesInput typeText:eyeColor]; + + XCUIElement *showInput = _window.textFields[@"testInput-shoe"]; + [showInput click]; + [showInput typeText:shoeSize]; + + return [NSString stringWithFormat:format, name, age, eyeColor, shoeSize]; } -- (void)testShouldMergeItemsInAsyncStorage { - [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; - - [_mergeItemButton click]; - - XCUIElement *saveItemButton = _window.buttons[@"saveItem_button"].staticTexts.firstMatch; - XCUIElement *restoreItemButton = _window.buttons[@"restoreItem_button"].staticTexts.firstMatch; - XCUIElement *mergeItemButton = _window.buttons[@"mergeItem_button"].staticTexts.firstMatch; - XCUIElement *storyText = _window.staticTexts[@"storyTextView"]; - - NSString *messageFormat = @"%@ is %@, has %@ eyes and shoe size of %@."; - - NSString *story = [self performInputWithFormat:messageFormat]; - [saveItemButton click]; - [_restartButton click]; - [restoreItemButton click]; - XCTAssertEqualObjects(storyText.label, story); - [_restartButton click]; - - // merging here - - NSString *newStory = [self performInputWithFormat:messageFormat]; - [mergeItemButton click]; - [_restartButton click]; - [restoreItemButton click]; - XCTAssertEqualObjects(storyText.label, newStory); +- (void)testShouldMergeItemsInAsyncStorage +{ + [self sendTestAppCommand:@"rnc-asyncstorage://unset-delegate"]; + + [_mergeItemButton click]; + + XCUIElement *saveItemButton = _window.buttons[@"saveItem_button"].staticTexts.firstMatch; + XCUIElement *restoreItemButton = _window.buttons[@"restoreItem_button"].staticTexts.firstMatch; + XCUIElement *mergeItemButton = _window.buttons[@"mergeItem_button"].staticTexts.firstMatch; + XCUIElement *storyText = _window.staticTexts[@"storyTextView"]; + + NSString *messageFormat = @"%@ is %@, has %@ eyes and shoe size of %@."; + + NSString *story = [self performInputWithFormat:messageFormat]; + [saveItemButton click]; + [_restartButton click]; + [restoreItemButton click]; + XCTAssertEqualObjects(storyText.label, story); + [_restartButton click]; + + // merging here + + NSString *newStory = [self performInputWithFormat:messageFormat]; + [mergeItemButton click]; + [_restartButton click]; + [restoreItemButton click]; + XCTAssertEqualObjects(storyText.label, newStory); } -- (void)testMergeItemDelegate { - [self sendTestAppCommand:@"rnc-asyncstorage://set-delegate"]; - - [_mergeItemButton click]; - - XCUIElement *saveItemButton = _window.buttons[@"saveItem_button"].staticTexts.firstMatch; - XCUIElement *restoreItemButton = _window.buttons[@"restoreItem_button"].staticTexts.firstMatch; - XCUIElement *mergeItemButton = _window.buttons[@"mergeItem_button"].staticTexts.firstMatch; - XCUIElement *storyText = _window.staticTexts[@"storyTextView"]; - - NSString *story = [self performInputWithFormat:@"%@ from delegate is %@ from delegate, has %@ eyes and shoe size of %@."]; - [mergeItemButton click]; - [_restartButton click]; - [restoreItemButton click]; - XCTAssertEqualObjects(storyText.label, story); +- (void)testMergeItemDelegate +{ + [self sendTestAppCommand:@"rnc-asyncstorage://set-delegate"]; + + [_mergeItemButton click]; + + XCUIElement *saveItemButton = _window.buttons[@"saveItem_button"].staticTexts.firstMatch; + XCUIElement *restoreItemButton = _window.buttons[@"restoreItem_button"].staticTexts.firstMatch; + XCUIElement *mergeItemButton = _window.buttons[@"mergeItem_button"].staticTexts.firstMatch; + XCUIElement *storyText = _window.staticTexts[@"storyTextView"]; + + NSString *story = + [self performInputWithFormat: + @"%@ from delegate is %@ from delegate, has %@ eyes and shoe size of %@."]; + [mergeItemButton click]; + [_restartButton click]; + [restoreItemButton click]; + XCTAssertEqualObjects(storyText.label, story); } -- (void)sendTestAppCommand:(NSString *)URLString { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:URLString]]; - sleep(.25); +- (void)sendTestAppCommand:(NSString *)URLString +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:URLString]]; + sleep(.25); } @end diff --git a/example/windows/AsyncStorageExample/App.cpp b/example/windows/AsyncStorageExample/App.cpp index a71b41a5..566a8239 100644 --- a/example/windows/AsyncStorageExample/App.cpp +++ b/example/windows/AsyncStorageExample/App.cpp @@ -1,5 +1,7 @@ #include "pch.h" + #include "App.h" + #include "ReactPackageProvider.h" using namespace winrt::AsyncStorageExample; @@ -30,10 +32,11 @@ App::App() noexcept InstanceSettings().EnableDeveloperMenu(false); #endif - PackageProviders().Append(make()); // Includes all modules in this project + // Includes all modules in this project + PackageProviders().Append(make()); PackageProviders().Append(winrt::ReactNativeAsyncStorage::ReactPackageProvider()); - REACT_REGISTER_NATIVE_MODULE_PACKAGES(); //code-gen macro from autolink + REACT_REGISTER_NATIVE_MODULE_PACKAGES(); // code-gen macro from autolink InitializeComponent(); } diff --git a/example/windows/AsyncStorageExample/App.h b/example/windows/AsyncStorageExample/App.h index 765e03f2..173106a7 100644 --- a/example/windows/AsyncStorageExample/App.h +++ b/example/windows/AsyncStorageExample/App.h @@ -4,10 +4,7 @@ namespace winrt::AsyncStorageExample::implementation { - struct App : AppT - { + struct App : AppT { App() noexcept; }; -} // namespace winrt::AsyncStorageExample::implementation - - +} // namespace winrt::AsyncStorageExample::implementation diff --git a/example/windows/AsyncStorageExample/ReactPackageProvider.cpp b/example/windows/AsyncStorageExample/ReactPackageProvider.cpp index caeb04f5..7e8d2ed4 100644 --- a/example/windows/AsyncStorageExample/ReactPackageProvider.cpp +++ b/example/windows/AsyncStorageExample/ReactPackageProvider.cpp @@ -1,5 +1,7 @@ #include "pch.h" + #include "ReactPackageProvider.h" + #include "NativeModules.h" using namespace winrt::Microsoft::ReactNative; @@ -7,9 +9,9 @@ using namespace winrt::Microsoft::ReactNative; namespace winrt::AsyncStorageExample::implementation { -void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept -{ - AddAttributedModules(packageBuilder); -} + void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept + { + AddAttributedModules(packageBuilder); + } -} // namespace winrt::AsyncStorageExample::implementation +} // namespace winrt::AsyncStorageExample::implementation diff --git a/example/windows/AsyncStorageExample/ReactPackageProvider.h b/example/windows/AsyncStorageExample/ReactPackageProvider.h index c8226835..f8c164bc 100644 --- a/example/windows/AsyncStorageExample/ReactPackageProvider.h +++ b/example/windows/AsyncStorageExample/ReactPackageProvider.h @@ -7,12 +7,9 @@ using namespace winrt::Microsoft::ReactNative; namespace winrt::AsyncStorageExample::implementation { - struct ReactPackageProvider : winrt::implements - { - public: // IReactPackageProvider + struct ReactPackageProvider : winrt::implements { + public: // IReactPackageProvider void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; }; -} // namespace winrt::AsyncStorageExample::implementation - - +} // namespace winrt::AsyncStorageExample::implementation diff --git a/example/windows/AsyncStorageExample/pch.h b/example/windows/AsyncStorageExample/pch.h index 4c780d37..d38af21f 100644 --- a/example/windows/AsyncStorageExample/pch.h +++ b/example/windows/AsyncStorageExample/pch.h @@ -6,6 +6,13 @@ #include #include #include + +#include +#include +#include +#include +#include +#include #include #include #include @@ -16,11 +23,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include + #include "nativeModules.g.h" #include "winrt/ReactNativeAsyncStorage.h" diff --git a/ios/RNCAsyncStorage.h b/ios/RNCAsyncStorage.h index bbbc0655..ead8ec73 100644 --- a/ios/RNCAsyncStorage.h +++ b/ios/RNCAsyncStorage.h @@ -5,8 +5,11 @@ * LICENSE file in the root directory of this source tree. */ +#import + #import #import + #import "RNCAsyncStorageDelegate.h" /** @@ -20,7 +23,7 @@ * * Keys and values must always be strings or an error is returned. */ -@interface RNCAsyncStorage : NSObject +@interface RNCAsyncStorage : NSObject @property (nonatomic, weak, nullable) id delegate; @@ -34,10 +37,12 @@ // For clearing data when the bridge may not exist, e.g. when logging out. + (void)clearAllData; -// Grab data from the cache. ResponseBlock result array will have an error at position 0, and an array of arrays at position 1. +// Grab data from the cache. ResponseBlock result array will have an error at position 0, and an +// array of arrays at position 1. - (void)multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback; // Add multiple key value pairs to the cache. -- (void)multiSet:(NSArray *> *)kvPairs callback:(RCTResponseSenderBlock)callback; +- (void)multiSet:(NSArray *> *)kvPairs + callback:(RCTResponseSenderBlock)callback; @end diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.m index 02369b79..499c9597 100644 --- a/ios/RNCAsyncStorage.m +++ b/ios/RNCAsyncStorage.m @@ -7,8 +7,6 @@ #import "RNCAsyncStorage.h" -#import - #import #import @@ -25,16 +23,17 @@ static NSDictionary *RCTErrorForKey(NSString *key) { - if (![key isKindOfClass:[NSString class]]) { - return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key}); - } else if (key.length < 1) { - return RCTMakeAndLogError(@"Invalid key - must be at least one character. Key: ", key, @{@"key": key}); - } else { - return nil; - } + if (![key isKindOfClass:[NSString class]]) { + return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key}); + } else if (key.length < 1) { + return RCTMakeAndLogError( + @"Invalid key - must be at least one character. Key: ", key, @{@"key": key}); + } else { + return nil; + } } -static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString* path, NSNumber* isExcluded) +static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString *path, NSNumber *isExcluded) { NSFileManager *fileManager = [[NSFileManager alloc] init]; @@ -43,9 +42,11 @@ static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString* path, NSNumber* isExc BOOL success = false; if (isDir && exists) { - NSURL* pathUrl = [NSURL fileURLWithPath:path]; + NSURL *pathUrl = [NSURL fileURLWithPath:path]; NSError *error = nil; - success = [pathUrl setResourceValue:isExcluded forKey:NSURLIsExcludedFromBackupKey error:&error]; + success = [pathUrl setResourceValue:isExcluded + forKey:NSURLIsExcludedFromBackupKey + error:&error]; if (!success) { NSLog(@"Could not exclude AsyncStorage dir from backup %@", error); @@ -56,176 +57,202 @@ static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString* path, NSNumber* isExc static void RCTAppendError(NSDictionary *error, NSMutableArray **errors) { - if (error && errors) { - if (!*errors) { - *errors = [NSMutableArray new]; + if (error && errors) { + if (!*errors) { + *errors = [NSMutableArray new]; + } + [*errors addObject:error]; } - [*errors addObject:error]; - } } -static NSArray *RCTMakeErrors(NSArray> *results) { - NSMutableArray *errors; - for (id object in results) { - if ([object isKindOfClass:[NSError class]]) { - NSError *error = (NSError *)object; - NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil); - RCTAppendError(keyError, &errors); +static NSArray *RCTMakeErrors(NSArray> *results) +{ + NSMutableArray *errors; + for (id object in results) { + if ([object isKindOfClass:[NSError class]]) { + NSError *error = (NSError *)object; + NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil); + RCTAppendError(keyError, &errors); + } } - } - return errors; + return errors; } static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut) { - if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { - NSError *error; - NSStringEncoding encoding; - NSString *entryString = [NSString stringWithContentsOfFile:filePath usedEncoding:&encoding error:&error]; - NSDictionary *extraData = @{@"key": RCTNullIfNil(key)}; - - if (error) { - if (errorOut) *errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData); - return nil; - } + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + NSError *error; + NSStringEncoding encoding; + NSString *entryString = [NSString stringWithContentsOfFile:filePath + usedEncoding:&encoding + error:&error]; + NSDictionary *extraData = @{@"key": RCTNullIfNil(key)}; + + if (error) { + if (errorOut) { + *errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData); + } + return nil; + } - if (encoding != NSUTF8StringEncoding) { - if (errorOut) *errorOut = RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData); - return nil; + if (encoding != NSUTF8StringEncoding) { + if (errorOut) { + *errorOut = + RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData); + } + return nil; + } + return entryString; } - return entryString; - } - return nil; + return nil; } // DO NOT USE // This is used internally to migrate data from the old file location to the new one. // Please use `RCTCreateStorageDirectoryPath` instead -static NSString *RCTCreateStorageDirectoryPath_deprecated(NSString *storageDir) { +static NSString *RCTCreateStorageDirectoryPath_deprecated(NSString *storageDir) +{ NSString *storageDirectoryPath; - #if TARGET_OS_TV - storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; - #else - storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; - #endif +#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 *RCTCreateStorageDirectoryPath(NSString *storageDir) { - NSString *storageDirectoryPath = @""; +static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir) +{ + NSString *storageDirectoryPath = @""; - #if TARGET_OS_TV - storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; - #else - storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject; - // We should use the "Application Support/[bundleID]" folder for persistent data storage that's hidden from users - storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]; - #endif +#if TARGET_OS_TV + storageDirectoryPath = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; +#else + storageDirectoryPath = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) + .firstObject; + // We should use the "Application Support/[bundleID]" folder for persistent data storage that's + // hidden from users + storageDirectoryPath = [storageDirectoryPath + stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]; +#endif - // Per Apple's docs, all app content in Application Support must be within a subdirectory of the app's bundle identifier - storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir]; + // Per Apple's docs, all app content in Application Support must be within a subdirectory of the + // app's bundle identifier + storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir]; - return storageDirectoryPath; + return storageDirectoryPath; } static NSString *RCTGetStorageDirectory() { - static NSString *storageDirectory = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory); - }); - return storageDirectory; + static NSString *storageDirectory = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory); + }); + return storageDirectory; } static NSString *RCTCreateManifestFilePath(NSString *storageDirectory) { - return [storageDirectory stringByAppendingPathComponent:RCTManifestFileName]; + return [storageDirectory stringByAppendingPathComponent:RCTManifestFileName]; } static NSString *RCTGetManifestFilePath() { - static NSString *manifestFilePath = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory); - }); - return manifestFilePath; + static NSString *manifestFilePath = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory); + }); + return manifestFilePath; } // Only merges objects - all other types are just clobbered (including arrays) static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source) { - BOOL modified = NO; - for (NSString *key in source) { - id sourceValue = source[key]; - id destinationValue = destination[key]; - if ([sourceValue isKindOfClass:[NSDictionary class]]) { - if ([destinationValue isKindOfClass:[NSDictionary class]]) { - if ([destinationValue classForCoder] != [NSMutableDictionary class]) { - destinationValue = [destinationValue mutableCopy]; - } - if (RCTMergeRecursive(destinationValue, sourceValue)) { - destination[key] = destinationValue; - modified = YES; + BOOL modified = NO; + for (NSString *key in source) { + id sourceValue = source[key]; + id destinationValue = destination[key]; + if ([sourceValue isKindOfClass:[NSDictionary class]]) { + if ([destinationValue isKindOfClass:[NSDictionary class]]) { + if ([destinationValue classForCoder] != [NSMutableDictionary class]) { + destinationValue = [destinationValue mutableCopy]; + } + if (RCTMergeRecursive(destinationValue, sourceValue)) { + destination[key] = destinationValue; + modified = YES; + } + } else { + destination[key] = [sourceValue copy]; + modified = YES; + } + } else if (![source isEqual:destinationValue]) { + destination[key] = [sourceValue copy]; + modified = YES; } - } else { - destination[key] = [sourceValue copy]; - modified = YES; - } - } else if (![source isEqual:destinationValue]) { - destination[key] = [sourceValue copy]; - modified = YES; } - } - return modified; + return modified; } static dispatch_queue_t RCTGetMethodQueue() { - // We want all instances to share the same queue since they will be reading/writing the same files. - static dispatch_queue_t queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL); - }); - return queue; + // We want all instances to share the same queue since they will be reading/writing the same + // files. + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = + dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL); + }); + return queue; } static NSCache *RCTGetCache() { - // We want all instances to share the same cache since they will be reading/writing the same files. - static NSCache *cache; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cache = [NSCache new]; - cache.totalCostLimit = 2 * 1024 * 1024; // 2MB + // We want all instances to share the same cache since they will be reading/writing the same + // files. + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [NSCache new]; + cache.totalCostLimit = 2 * 1024 * 1024; // 2MB #if !TARGET_OS_OSX - // Clear cache in the event of a memory warning - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) { - [cache removeAllObjects]; - }]; -#endif // !TARGET_OS_OSX - }); - return cache; + // Clear cache in the event of a memory warning + [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidReceiveMemoryWarningNotification + object:nil + queue:nil + usingBlock:^(__unused NSNotification *note) { + [cache removeAllObjects]; + }]; +#endif // !TARGET_OS_OSX + }); + return cache; } static BOOL RCTHasCreatedStorageDirectory = NO; static NSDictionary *RCTDeleteStorageDirectory() { - NSError *error; - [[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]; - RCTHasCreatedStorageDirectory = NO; - return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil; + NSError *error; + [[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error]; + RCTHasCreatedStorageDirectory = NO; + 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]; + NSDictionary *attributes = + [[NSFileManager defaultManager] attributesOfItemAtPath:manifestFilePath error:nil]; + return [attributes fileModificationDate]; } /** @@ -233,47 +260,59 @@ static dispatch_queue_t RCTGetMethodQueue() */ static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *error) { - RCTLogWarn(@"%@: %@", reason, error ? error.description : @""); + RCTLogWarn(@"%@: %@", reason, error ? error.description : @""); } static void RCTStorageDirectoryCleanupOld(NSString *oldDirectoryPath) { - NSError *error; - if (![[NSFileManager defaultManager] removeItemAtPath:oldDirectoryPath error:&error]) { - RCTStorageDirectoryMigrationLogError(@"Failed to remove old storage directory during migration", error); - } + NSError *error; + if (![[NSFileManager defaultManager] removeItemAtPath:oldDirectoryPath error:&error]) { + RCTStorageDirectoryMigrationLogError( + @"Failed to remove old storage directory during migration", error); + } } static void _createStorageDirectory(NSString *storageDirectory, NSError **error) { - [[NSFileManager defaultManager] createDirectoryAtPath:storageDirectory - withIntermediateDirectories:YES - attributes:nil - error:error]; -} - -static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath, NSString *newDirectoryPath, BOOL shouldCleanupOldDirectory) -{ - NSError *error; - // Migrate data by copying old storage directory to new storage directory location - if (![[NSFileManager defaultManager] copyItemAtPath:oldDirectoryPath toPath:newDirectoryPath error:&error]) { - // the new storage directory "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" seems unable to migrate - // because folder "Application Support/[bundleID]" doesn't exist.. create this folder and attempt folder copying again - if (error != nil && error.code == 4 && [newDirectoryPath isEqualToString:RCTGetStorageDirectory()]) { - NSError *error = nil; - _createStorageDirectory(RCTCreateStorageDirectoryPath(@""), &error); - if (error == nil) { - RCTStorageDirectoryMigrate(oldDirectoryPath, newDirectoryPath, shouldCleanupOldDirectory); - } else { - RCTStorageDirectoryMigrationLogError(@"Failed to create storage directory during migration.", error); - } - } else { - RCTStorageDirectoryMigrationLogError(@"Failed to copy old storage directory to new storage directory location during migration", error); + [[NSFileManager defaultManager] createDirectoryAtPath:storageDirectory + withIntermediateDirectories:YES + attributes:nil + error:error]; +} + +static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath, + NSString *newDirectoryPath, + BOOL shouldCleanupOldDirectory) +{ + NSError *error; + // Migrate data by copying old storage directory to new storage directory location + if (![[NSFileManager defaultManager] copyItemAtPath:oldDirectoryPath + toPath:newDirectoryPath + error:&error]) { + // the new storage directory "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" seems + // unable to migrate because folder "Application Support/[bundleID]" doesn't exist.. create + // this folder and attempt folder copying again + if (error != nil && error.code == 4 && + [newDirectoryPath isEqualToString:RCTGetStorageDirectory()]) { + NSError *error = nil; + _createStorageDirectory(RCTCreateStorageDirectoryPath(@""), &error); + if (error == nil) { + RCTStorageDirectoryMigrate( + oldDirectoryPath, newDirectoryPath, shouldCleanupOldDirectory); + } else { + RCTStorageDirectoryMigrationLogError( + @"Failed to create storage directory during migration.", error); + } + } else { + RCTStorageDirectoryMigrationLogError( + @"Failed to copy old storage directory to new storage directory location during " + @"migration", + error); + } + } else if (shouldCleanupOldDirectory) { + // If copying succeeds, remove old storage directory + RCTStorageDirectoryCleanupOld(oldDirectoryPath); } - } else if (shouldCleanupOldDirectory) { - // If copying succeeds, remove old storage directory - RCTStorageDirectoryCleanupOld(oldDirectoryPath); - } } /** @@ -281,484 +320,536 @@ static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath, NSString *new * Check that data is migrated from the old location to the new location * fromStorageDirectory: the directory where the older data lives * toStorageDirectory: the directory where the new data should live - * 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 + * 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 */ -static void RCTStorageDirectoryMigrationCheck(NSString *fromStorageDirectory, NSString *toStorageDirectory, BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory) -{ - NSError *error; - BOOL isDir; - NSFileManager *fileManager = [NSFileManager defaultManager]; - // If the old directory exists, it means we may need to migrate old data to the new directory - if ([fileManager fileExistsAtPath:fromStorageDirectory isDirectory:&isDir] && isDir) { - // Check if the new storage directory location already exists - if ([fileManager fileExistsAtPath:toStorageDirectory]) { - // 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 - if ([RCTManifestModificationDate(RCTCreateManifestFilePath(toStorageDirectory)) compare:RCTManifestModificationDate(RCTCreateManifestFilePath(fromStorageDirectory))] == 1) { - // If new location has been modified more recently, simply clean out old data - if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) { - RCTStorageDirectoryCleanupOld(fromStorageDirectory); - } - } else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) { - // If old location has been modified more recently, remove new storage and migrate - if (![fileManager removeItemAtPath:toStorageDirectory error:&error]) { - RCTStorageDirectoryMigrationLogError(@"Failed to remove new storage directory during migration", error); +static void +RCTStorageDirectoryMigrationCheck(NSString *fromStorageDirectory, + NSString *toStorageDirectory, + BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory) +{ + NSError *error; + BOOL isDir; + NSFileManager *fileManager = [NSFileManager defaultManager]; + // If the old directory exists, it means we may need to migrate old data to the new directory + if ([fileManager fileExistsAtPath:fromStorageDirectory isDirectory:&isDir] && isDir) { + // Check if the new storage directory location already exists + if ([fileManager fileExistsAtPath:toStorageDirectory]) { + // 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 + if ([RCTManifestModificationDate(RCTCreateManifestFilePath(toStorageDirectory)) + compare:RCTManifestModificationDate( + RCTCreateManifestFilePath(fromStorageDirectory))] == 1) { + // If new location has been modified more recently, simply clean out old data + if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) { + RCTStorageDirectoryCleanupOld(fromStorageDirectory); + } + } else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) { + // If old location has been modified more recently, remove new storage and migrate + if (![fileManager removeItemAtPath:toStorageDirectory error:&error]) { + RCTStorageDirectoryMigrationLogError( + @"Failed to remove new storage directory during migration", error); + } else { + RCTStorageDirectoryMigrate(fromStorageDirectory, + toStorageDirectory, + shouldCleanupOldDirectoryAndOverwriteNewDirectory); + } + } } else { - RCTStorageDirectoryMigrate(fromStorageDirectory, toStorageDirectory, shouldCleanupOldDirectoryAndOverwriteNewDirectory); + // If new storage location doesn't exist, migrate data + RCTStorageDirectoryMigrate(fromStorageDirectory, + toStorageDirectory, + shouldCleanupOldDirectoryAndOverwriteNewDirectory); } - } - } else { - // If new storage location doesn't exist, migrate data - RCTStorageDirectoryMigrate(fromStorageDirectory, toStorageDirectory, shouldCleanupOldDirectoryAndOverwriteNewDirectory); } - } } #pragma mark - RNCAsyncStorage -@implementation RNCAsyncStorage -{ - BOOL _haveSetup; - // The manifest is a dictionary of all keys with small values inlined. Null values indicate values that are stored - // 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 *_manifest; +@implementation RNCAsyncStorage { + BOOL _haveSetup; + // The manifest is a dictionary of all keys with small values inlined. Null values indicate + // values that are stored 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 *_manifest; } + (BOOL)requiresMainQueueSetup { - return NO; + return NO; } - (instancetype)init { - if (!(self = [super init])) { - return nil; - } + if (!(self = [super init])) { + return nil; + } - // First migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" to "Documents/.../RCTAsyncLocalStorage_V1" - RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory), RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), YES); + // First migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" to + // "Documents/.../RCTAsyncLocalStorage_V1" + RCTStorageDirectoryMigrationCheck( + RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory), + RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), + YES); - // Then migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" - RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), RCTCreateStorageDirectoryPath(RCTStorageDirectory), NO); + // Then migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to "Application + // Support/[bundleID]/RCTAsyncLocalStorage_V1" + RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), + RCTCreateStorageDirectoryPath(RCTStorageDirectory), + NO); - return self; + return self; } RCT_EXPORT_MODULE() - (dispatch_queue_t)methodQueue { - return RCTGetMethodQueue(); + return RCTGetMethodQueue(); } - (void)clearAllData { - dispatch_async(RCTGetMethodQueue(), ^{ - [self->_manifest removeAllObjects]; - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - }); + dispatch_async(RCTGetMethodQueue(), ^{ + [self->_manifest removeAllObjects]; + [RCTGetCache() removeAllObjects]; + RCTDeleteStorageDirectory(); + }); } + (void)clearAllData { - dispatch_async(RCTGetMethodQueue(), ^{ - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - }); + dispatch_async(RCTGetMethodQueue(), ^{ + [RCTGetCache() removeAllObjects]; + RCTDeleteStorageDirectory(); + }); } - (void)invalidate { - if (_clearOnInvalidate) { - [RCTGetCache() removeAllObjects]; - RCTDeleteStorageDirectory(); - } - _clearOnInvalidate = NO; - [_manifest removeAllObjects]; - _haveSetup = NO; + if (_clearOnInvalidate) { + [RCTGetCache() removeAllObjects]; + RCTDeleteStorageDirectory(); + } + _clearOnInvalidate = NO; + [_manifest removeAllObjects]; + _haveSetup = NO; } - (BOOL)isValid { - return _haveSetup; + return _haveSetup; } - (void)dealloc { - [self invalidate]; + [self invalidate]; } - (NSString *)_filePathForKey:(NSString *)key { - NSString *safeFileName = RCTMD5Hash(key); - return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName]; + NSString *safeFileName = RCTMD5Hash(key); + return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName]; } - (NSDictionary *)_ensureSetup { - RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread"); + RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread"); #if TARGET_OS_TV - RCTLogWarn(@"Persistent storage is not supported on tvOS, your data may be removed at any point."); + RCTLogWarn( + @"Persistent storage is not supported on tvOS, your data may be removed at any point."); #endif - NSError *error = nil; - if (!RCTHasCreatedStorageDirectory) { - _createStorageDirectory(RCTGetStorageDirectory(), &error); - if (error) { - return RCTMakeError(@"Failed to create storage directory.", error, nil); - } - RCTHasCreatedStorageDirectory = YES; - } - - if (!_haveSetup) { - // iCloud backup exclusion - NSNumber* isExcludedFromBackup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTAsyncStorageExcludeFromBackup"]; - if (isExcludedFromBackup == nil) { - // by default, we want to exclude AsyncStorage data from backup - isExcludedFromBackup = @YES; - } - RCTAsyncStorageSetExcludedFromBackup(RCTCreateStorageDirectoryPath(RCTStorageDirectory), isExcludedFromBackup); - - NSDictionary *errorOut = nil; - NSString *serialized = RCTReadFile(RCTCreateStorageDirectoryPath(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 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); - 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]; - } + NSError *error = nil; + if (!RCTHasCreatedStorageDirectory) { + _createStorageDirectory(RCTGetStorageDirectory(), &error); + if (error) { + return RCTMakeError(@"Failed to create storage directory.", error, nil); + } + RCTHasCreatedStorageDirectory = YES; } - _haveSetup = YES; - } - return nil; + if (!_haveSetup) { + // iCloud backup exclusion + NSNumber *isExcludedFromBackup = + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTAsyncStorageExcludeFromBackup"]; + if (isExcludedFromBackup == nil) { + // by default, we want to exclude AsyncStorage data from backup + isExcludedFromBackup = @YES; + } + RCTAsyncStorageSetExcludedFromBackup(RCTCreateStorageDirectoryPath(RCTStorageDirectory), + isExcludedFromBackup); + + NSDictionary *errorOut = nil; + NSString *serialized = RCTReadFile(RCTCreateStorageDirectoryPath(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 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); + 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; } - (NSDictionary *)_writeManifest:(NSMutableArray **)errors { - NSError *error; - NSString *serialized = RCTJSONStringify(_manifest, &error); - [serialized writeToFile:RCTCreateStorageDirectoryPath(RCTGetManifestFilePath()) atomically:YES encoding:NSUTF8StringEncoding error:&error]; - NSDictionary *errorOut; - if (error) { - errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil); - RCTAppendError(errorOut, errors); - } - return errorOut; + NSError *error; + NSString *serialized = RCTJSONStringify(_manifest, &error); + [serialized writeToFile:RCTCreateStorageDirectoryPath(RCTGetManifestFilePath()) + atomically:YES + encoding:NSUTF8StringEncoding + error:&error]; + NSDictionary *errorOut; + if (error) { + errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil); + RCTAppendError(errorOut, errors); + } + return errorOut; } - (NSDictionary *)_appendItemForKey:(NSString *)key toArray:(NSMutableArray *> *)result { - NSDictionary *errorOut = RCTErrorForKey(key); - if (errorOut) { + NSDictionary *errorOut = RCTErrorForKey(key); + if (errorOut) { + return errorOut; + } + NSString *value = [self _getValueForKey:key errorOut:&errorOut]; + [result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure. return errorOut; - } - NSString *value = [self _getValueForKey:key errorOut:&errorOut]; - [result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure. - return errorOut; } - (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut { - NSString *value = _manifest[key]; // nil means missing, null means there may be a data file, else: NSString - if (value == (id)kCFNull) { - value = [RCTGetCache() objectForKey:key]; - if (!value) { - NSString *filePath = [self _filePathForKey:key]; - value = RCTReadFile(filePath, key, errorOut); - if (value) { - [RCTGetCache() setObject:value forKey:key cost:value.length]; - } else { - // file does not exist after all, so remove from manifest (no need to save - // manifest immediately though, as cost of checking again next time is negligible) - [_manifest removeObjectForKey:key]; - } + NSString *value = + _manifest[key]; // nil means missing, null means there may be a data file, else: NSString + if (value == (id)kCFNull) { + value = [RCTGetCache() objectForKey:key]; + if (!value) { + NSString *filePath = [self _filePathForKey:key]; + value = RCTReadFile(filePath, key, errorOut); + if (value) { + [RCTGetCache() setObject:value forKey:key cost:value.length]; + } else { + // file does not exist after all, so remove from manifest (no need to save + // manifest immediately though, as cost of checking again next time is negligible) + [_manifest removeObjectForKey:key]; + } + } } - } - return value; + return value; } - (NSDictionary *)_writeEntry:(NSArray *)entry changedManifest:(BOOL *)changedManifest { - if (entry.count != 2) { - return RCTMakeAndLogError(@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil); - } - NSString *key = entry[0]; - NSDictionary *errorOut = RCTErrorForKey(key); - if (errorOut) { + if (entry.count != 2) { + return RCTMakeAndLogError( + @"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil); + } + NSString *key = entry[0]; + NSDictionary *errorOut = RCTErrorForKey(key); + if (errorOut) { + return errorOut; + } + NSString *value = entry[1]; + NSString *filePath = [self _filePathForKey:key]; + NSError *error; + if (value.length <= RCTInlineValueThreshold) { + if (_manifest[key] == (id)kCFNull) { + // If the value already existed but wasn't inlined, remove the old file. + [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; + [RCTGetCache() removeObjectForKey:key]; + } + *changedManifest = YES; + _manifest[key] = value; + return nil; + } + [value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]; + [RCTGetCache() setObject:value forKey:key cost:value.length]; + if (error) { + errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key}); + } else if (_manifest[key] != (id)kCFNull) { + *changedManifest = YES; + _manifest[key] = (id)kCFNull; + } return errorOut; - } - NSString *value = entry[1]; - NSString *filePath = [self _filePathForKey:key]; - NSError *error; - if (value.length <= RCTInlineValueThreshold) { - if (_manifest[key] == (id)kCFNull) { - // If the value already existed but wasn't inlined, remove the old file. - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - [RCTGetCache() removeObjectForKey:key]; - } - *changedManifest = YES; - _manifest[key] = value; - return nil; - } - [value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]; - [RCTGetCache() setObject:value forKey:key cost:value.length]; - if (error) { - errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key}); - } else if (_manifest[key] != (id)kCFNull) { - *changedManifest = YES; - _manifest[key] = (id)kCFNull; - } - return errorOut; } - (void)_multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback - getter:(NSString *(^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue -{ - NSMutableArray *errors; - NSMutableArray *> *result = [NSMutableArray arrayWithCapacity:keys.count]; - for (NSUInteger i = 0; i < keys.count; ++i) { - NSString *key = keys[i]; - id keyError; - id value = getValue(i, key, &keyError); - [result addObject:@[key, RCTNullIfNil(value)]]; - RCTAppendError(keyError, &errors); - } - callback(@[RCTNullIfNil(errors), result]); + getter:(NSString * (^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue +{ + NSMutableArray *errors; + NSMutableArray *> *result = [NSMutableArray arrayWithCapacity:keys.count]; + for (NSUInteger i = 0; i < keys.count; ++i) { + NSString *key = keys[i]; + id keyError; + id value = getValue(i, key, &keyError); + [result addObject:@[key, RCTNullIfNil(value)]]; + RCTAppendError(keyError, &errors); + } + callback(@[RCTNullIfNil(errors), result]); } - (BOOL)_passthroughDelegate { - return [self.delegate respondsToSelector:@selector(isPassthrough)] && self.delegate.isPassthrough; + return + [self.delegate respondsToSelector:@selector(isPassthrough)] && self.delegate.isPassthrough; } #pragma mark - Exported JS Functions +// clang-format off RCT_EXPORT_METHOD(multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback) -{ - if (self.delegate != nil) { - [self.delegate valuesForKeys:keys completion:^(NSArray> *valuesOrErrors) { - [self _multiGet:keys - callback:callback - getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) { - id valueOrError = valuesOrErrors[i]; - if ([valueOrError isKindOfClass:[NSError class]]) { - NSError *error = (NSError *)valueOrError; - NSDictionary *extraData = @{@"key": RCTNullIfNil(key)}; - *errorOut = RCTMakeError(error.localizedDescription, error, extraData); - return nil; - } else { - return [valueOrError isKindOfClass:[NSString class]] - ? (NSString *)valueOrError - : nil; - } - }]; - }]; - - if (![self _passthroughDelegate]) { - return; - } - } - - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut], (id)kCFNull]); - return; - } - [self _multiGet:keys - callback:callback - getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) { - return [self _getValueForKey:key errorOut:errorOut]; - }]; +// clang-format on +{ + if (self.delegate != nil) { + [self.delegate + valuesForKeys:keys + completion:^(NSArray> *valuesOrErrors) { + [self _multiGet:keys + callback:callback + getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) { + id valueOrError = valuesOrErrors[i]; + if ([valueOrError isKindOfClass:[NSError class]]) { + NSError *error = (NSError *)valueOrError; + NSDictionary *extraData = @{@"key": RCTNullIfNil(key)}; + *errorOut = + RCTMakeError(error.localizedDescription, error, extraData); + return nil; + } else { + return [valueOrError isKindOfClass:[NSString class]] + ? (NSString *)valueOrError + : nil; + } + }]; + }]; + + if (![self _passthroughDelegate]) { + return; + } + } + + NSDictionary *errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut], (id)kCFNull]); + return; + } + [self _multiGet:keys + callback:callback + getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) { + return [self _getValueForKey:key errorOut:errorOut]; + }]; } +// clang-format off RCT_EXPORT_METHOD(multiSet:(NSArray *> *)kvPairs callback:(RCTResponseSenderBlock)callback) -{ - if (self.delegate != nil) { - NSMutableArray *keys = [NSMutableArray arrayWithCapacity:kvPairs.count]; - NSMutableArray *values = [NSMutableArray arrayWithCapacity:kvPairs.count]; +// clang-format on +{ + if (self.delegate != nil) { + NSMutableArray *keys = [NSMutableArray arrayWithCapacity:kvPairs.count]; + NSMutableArray *values = [NSMutableArray arrayWithCapacity:kvPairs.count]; + for (NSArray *entry in kvPairs) { + [keys addObject:entry[0]]; + [values addObject:entry[1]]; + } + [self.delegate setValues:values + forKeys:keys + completion:^(NSArray> *results) { + NSArray *errors = RCTMakeErrors(results); + callback(@[RCTNullIfNil(errors)]); + }]; + + if (![self _passthroughDelegate]) { + return; + } + } + + NSDictionary *errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut]]); + return; + } + BOOL changedManifest = NO; + NSMutableArray *errors; for (NSArray *entry in kvPairs) { - [keys addObject:entry[0]]; - [values addObject:entry[1]]; - } - [self.delegate setValues:values forKeys:keys completion:^(NSArray> *results) { - NSArray *errors = RCTMakeErrors(results); - callback(@[RCTNullIfNil(errors)]); - }]; - - if (![self _passthroughDelegate]) { - return; - } - } - - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut]]); - return; - } - BOOL changedManifest = NO; - NSMutableArray *errors; - for (NSArray *entry in kvPairs) { - NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest]; - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[RCTNullIfNil(errors)]); + NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest]; + RCTAppendError(keyError, &errors); + } + if (changedManifest) { + [self _writeManifest:&errors]; + } + callback(@[RCTNullIfNil(errors)]); } +// clang-format off RCT_EXPORT_METHOD(multiMerge:(NSArray *> *)kvPairs callback:(RCTResponseSenderBlock)callback) -{ - if (self.delegate != nil) { - NSMutableArray *keys = [NSMutableArray arrayWithCapacity:kvPairs.count]; - NSMutableArray *values = [NSMutableArray arrayWithCapacity:kvPairs.count]; - for (NSArray *entry in kvPairs) { - [keys addObject:entry[0]]; - [values addObject:entry[1]]; - } - [self.delegate mergeValues:values forKeys:keys completion:^(NSArray> *results) { - NSArray *errors = RCTMakeErrors(results); - callback(@[RCTNullIfNil(errors)]); - }]; - - if (![self _passthroughDelegate]) { - return; - } - } - - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut]]); - return; - } - BOOL changedManifest = NO; - NSMutableArray *errors; - for (__strong NSArray *entry in kvPairs) { - NSDictionary *keyError; - NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError]; - if (!keyError) { - if (value) { - NSError *jsonError; - NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError); - if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) { - entry = @[entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL))]; +// clang-format on +{ + if (self.delegate != nil) { + NSMutableArray *keys = [NSMutableArray arrayWithCapacity:kvPairs.count]; + NSMutableArray *values = [NSMutableArray arrayWithCapacity:kvPairs.count]; + for (NSArray *entry in kvPairs) { + [keys addObject:entry[0]]; + [values addObject:entry[1]]; } - if (jsonError) { - keyError = RCTJSErrorFromNSError(jsonError); + [self.delegate mergeValues:values + forKeys:keys + completion:^(NSArray> *results) { + NSArray *errors = RCTMakeErrors(results); + callback(@[RCTNullIfNil(errors)]); + }]; + + if (![self _passthroughDelegate]) { + return; } - } - if (!keyError) { - keyError = [self _writeEntry:entry changedManifest:&changedManifest]; - } } - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[RCTNullIfNil(errors)]); + + NSDictionary *errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut]]); + return; + } + BOOL changedManifest = NO; + NSMutableArray *errors; + for (__strong NSArray *entry in kvPairs) { + NSDictionary *keyError; + NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError]; + if (!keyError) { + if (value) { + NSError *jsonError; + NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError); + if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) { + entry = @[entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL))]; + } + if (jsonError) { + keyError = RCTJSErrorFromNSError(jsonError); + } + } + if (!keyError) { + keyError = [self _writeEntry:entry changedManifest:&changedManifest]; + } + } + RCTAppendError(keyError, &errors); + } + if (changedManifest) { + [self _writeManifest:&errors]; + } + callback(@[RCTNullIfNil(errors)]); } +// clang-format off RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys callback:(RCTResponseSenderBlock)callback) +// clang-format on { - if (self.delegate != nil) { - [self.delegate removeValuesForKeys:keys completion:^(NSArray> *results) { - NSArray *errors = RCTMakeErrors(results); - callback(@[RCTNullIfNil(errors)]); - }]; - - if (![self _passthroughDelegate]) { - return; - } - } - - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[@[errorOut]]); - return; - } - NSMutableArray *errors; - BOOL changedManifest = NO; - for (NSString *key in keys) { - NSDictionary *keyError = RCTErrorForKey(key); - if (!keyError) { - if (_manifest[key] == (id)kCFNull) { - NSString *filePath = [self _filePathForKey:key]; - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - [RCTGetCache() removeObjectForKey:key]; - } - if (_manifest[key]) { - changedManifest = YES; - [_manifest removeObjectForKey:key]; - } - } - RCTAppendError(keyError, &errors); - } - if (changedManifest) { - [self _writeManifest:&errors]; - } - callback(@[RCTNullIfNil(errors)]); + if (self.delegate != nil) { + [self.delegate removeValuesForKeys:keys + completion:^(NSArray> *results) { + NSArray *errors = RCTMakeErrors(results); + callback(@[RCTNullIfNil(errors)]); + }]; + + if (![self _passthroughDelegate]) { + return; + } + } + + NSDictionary *errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[@[errorOut]]); + return; + } + NSMutableArray *errors; + BOOL changedManifest = NO; + for (NSString *key in keys) { + NSDictionary *keyError = RCTErrorForKey(key); + if (!keyError) { + if (_manifest[key] == (id)kCFNull) { + NSString *filePath = [self _filePathForKey:key]; + [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; + [RCTGetCache() removeObjectForKey:key]; + } + if (_manifest[key]) { + changedManifest = YES; + [_manifest removeObjectForKey:key]; + } + } + RCTAppendError(keyError, &errors); + } + if (changedManifest) { + [self _writeManifest:&errors]; + } + callback(@[RCTNullIfNil(errors)]); } +// clang-format off RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback) -{ - if (self.delegate != nil) { - [self.delegate removeAllValues:^(NSError *error) { - NSDictionary *result = nil; - if (error != nil) { - result = RCTMakeError(error.localizedDescription, error, nil); - } - callback(@[RCTNullIfNil(result)]); - }]; - return; - } +// clang-format on +{ + if (self.delegate != nil) { + [self.delegate removeAllValues:^(NSError *error) { + NSDictionary *result = nil; + if (error != nil) { + result = RCTMakeError(error.localizedDescription, error, nil); + } + callback(@[RCTNullIfNil(result)]); + }]; + return; + } - [_manifest removeAllObjects]; - [RCTGetCache() removeAllObjects]; - NSDictionary *error = RCTDeleteStorageDirectory(); - callback(@[RCTNullIfNil(error)]); + [_manifest removeAllObjects]; + [RCTGetCache() removeAllObjects]; + NSDictionary *error = RCTDeleteStorageDirectory(); + callback(@[RCTNullIfNil(error)]); } +// clang-format off RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback) +// clang-format on { - if (self.delegate != nil) { - [self.delegate allKeys:^(NSArray> *keys) { - callback(@[(id)kCFNull, keys]); - }]; + if (self.delegate != nil) { + [self.delegate allKeys:^(NSArray> *keys) { + callback(@[(id)kCFNull, keys]); + }]; - if (![self _passthroughDelegate]) { - return; + if (![self _passthroughDelegate]) { + return; + } } - } - NSDictionary *errorOut = [self _ensureSetup]; - if (errorOut) { - callback(@[errorOut, (id)kCFNull]); - } else { - callback(@[(id)kCFNull, _manifest.allKeys]); - } + NSDictionary *errorOut = [self _ensureSetup]; + if (errorOut) { + callback(@[errorOut, (id)kCFNull]); + } else { + callback(@[(id)kCFNull, _manifest.allKeys]); + } } @end diff --git a/ios/RNCAsyncStorageDelegate.h b/ios/RNCAsyncStorageDelegate.h index 84e129e1..8298099e 100644 --- a/ios/RNCAsyncStorageDelegate.h +++ b/ios/RNCAsyncStorageDelegate.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^RNCAsyncStorageCompletion)(NSError *_Nullable error); -typedef void (^RNCAsyncStorageResultCallback)(NSArray> * valuesOrErrors); +typedef void (^RNCAsyncStorageResultCallback)(NSArray> *valuesOrErrors); @protocol RNCAsyncStorageDelegate @@ -59,8 +59,7 @@ typedef void (^RNCAsyncStorageResultCallback)(NSArray> * valuesOrEr * @param keys Keys of values to return. * @param block Block to call with result. */ -- (void)valuesForKeys:(NSArray *)keys - completion:(RNCAsyncStorageResultCallback)block; +- (void)valuesForKeys:(NSArray *)keys completion:(RNCAsyncStorageResultCallback)block; @optional diff --git a/metro.config.windows.js b/metro.config.windows.js index b7fde303..146d07a0 100644 --- a/metro.config.windows.js +++ b/metro.config.windows.js @@ -31,7 +31,9 @@ module.exports = { `${(path.resolve(rnPath) + path.sep).replace(/[/\\]/g, '/')}.*`, ), new RegExp( - `${(path.resolve(__dirname, '../', 'node_modules/react-native') + path.sep).replace(/[/\\]/g, '/')}.*`, + `${( + path.resolve(__dirname, '../', 'node_modules/react-native') + path.sep + ).replace(/[/\\]/g, '/')}.*`, ), // This stops "react-native run-windows" from causing the metro server to crash if its already running diff --git a/package.json b/package.json index 3954819f..1c26d19e 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "url": "https://github.com/react-native-async-storage/async-storage.git" }, "scripts": { + "format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')", + "format:js": "prettier --write $(git ls-files '*.js' '*.json' '*.yml')", "prepare": "bob build", "start": "node node_modules/react-native/local-cli/cli.js start", "start:android": "react-native run-android --root example/", diff --git a/windows/ReactNativeAsyncStorage/DBStorage.cpp b/windows/ReactNativeAsyncStorage/DBStorage.cpp index ac18d8af..809f5d93 100644 --- a/windows/ReactNativeAsyncStorage/DBStorage.cpp +++ b/windows/ReactNativeAsyncStorage/DBStorage.cpp @@ -1,31 +1,39 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" + #include "DBStorage.h" -namespace winrt { +namespace winrt +{ using namespace Microsoft::ReactNative; using namespace Windows::ApplicationModel::Core; using namespace Windows::Foundation; using namespace Windows::Storage; -} // namespace winrt +} // namespace winrt -namespace { +namespace +{ - void InvokeError(const DBStorage::Callback& callback, const char* message) { + void InvokeError(const DBStorage::Callback &callback, const char *message) + { auto writer = winrt::MakeJSValueTreeWriter(); writer.WriteObjectBegin(); winrt::WriteProperty(writer, L"message", message); writer.WriteObjectEnd(); - callback(winrt::JSValueArray{ winrt::TakeJSValue(writer) }); + callback(winrt::JSValueArray{winrt::TakeJSValue(writer)}); } - using ExecCallback = int(SQLITE_CALLBACK*)(void*, int, char**, char**); + using ExecCallback = int(SQLITE_CALLBACK *)(void *, int, char **, char **); // Execute the provided SQLite statement (and optional execCallback & user data // in pv). On error, throw a runtime_error with the SQLite error message - void Exec(sqlite3* db, const char* statement, ExecCallback execCallback = nullptr, void* pv = nullptr) { - char* errMsg = nullptr; + void Exec(sqlite3 *db, + const char *statement, + ExecCallback execCallback = nullptr, + void *pv = nullptr) + { + char *errMsg = nullptr; int rc = sqlite3_exec(db, statement, execCallback, pv, &errMsg); if (errMsg) { std::runtime_error exception(errMsg); @@ -43,13 +51,13 @@ namespace { // Execute the provided SQLite statement (and optional execCallback & user data // in pv). On error, reports it to the callback and returns false. - bool Exec( - sqlite3* db, - const DBStorage::Callback& callback, - const char* statement, - ExecCallback execCallback = nullptr, - void* pv = nullptr) { - char* errMsg = nullptr; + bool Exec(sqlite3 *db, + const DBStorage::Callback &callback, + const char *statement, + ExecCallback execCallback = nullptr, + void *pv = nullptr) + { + char *errMsg = nullptr; int rc = sqlite3_exec(db, statement, execCallback, pv, &errMsg); if (errMsg) { InvokeError(callback, errMsg); @@ -65,19 +73,23 @@ namespace { // Convenience wrapper for using Exec with lambda expressions template - bool Exec(sqlite3* db, const DBStorage::Callback& callback, const char* statement, Fn& fn) { + bool Exec(sqlite3 *db, const DBStorage::Callback &callback, const char *statement, Fn &fn) + { return Exec( db, callback, statement, - [](void* pv, int i, char** x, char** y) { return (*static_cast(pv))(i, x, y); }, + [](void *pv, int i, char **x, char **y) { return (*static_cast(pv))(i, x, y); }, &fn); } // Checks that the args parameter is an array, that args.size() is less than // SQLITE_LIMIT_VARIABLE_NUMBER, and that every member of args is a string. // Invokes callback to report an error and returns false. - bool CheckArgs(sqlite3* db, const std::vector& args, const DBStorage::Callback& callback) { + bool CheckArgs(sqlite3 *db, + const std::vector &args, + const DBStorage::Callback &callback) + { int varLimit = sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1); auto argCount = args.size(); if (argCount > INT_MAX || static_cast(argCount) > varLimit) { @@ -98,25 +110,31 @@ namespace { // Commit() has not been called, rolls back the transactions // The provided sqlite connection handle & Callback must outlive // the Sqlite3Transaction object - class Sqlite3Transaction { - sqlite3* m_db{ nullptr }; - const DBStorage::Callback* m_callback{ nullptr }; + class Sqlite3Transaction + { + sqlite3 *m_db{nullptr}; + const DBStorage::Callback *m_callback{nullptr}; public: Sqlite3Transaction() = default; - Sqlite3Transaction(sqlite3* db, const DBStorage::Callback& callback) : m_db(db), m_callback(&callback) { + Sqlite3Transaction(sqlite3 *db, const DBStorage::Callback &callback) + : m_db(db), m_callback(&callback) + { if (!Exec(m_db, *m_callback, u8"BEGIN TRANSACTION")) { m_db = nullptr; m_callback = nullptr; } } - Sqlite3Transaction(const Sqlite3Transaction&) = delete; - Sqlite3Transaction(Sqlite3Transaction&& other) : m_db(other.m_db), m_callback(other.m_callback) { + Sqlite3Transaction(const Sqlite3Transaction &) = delete; + Sqlite3Transaction(Sqlite3Transaction &&other) + : m_db(other.m_db), m_callback(other.m_callback) + { other.m_db = nullptr; other.m_callback = nullptr; } - Sqlite3Transaction& operator=(const Sqlite3Transaction&) = delete; - Sqlite3Transaction& operator=(Sqlite3Transaction&& rhs) { + Sqlite3Transaction &operator=(const Sqlite3Transaction &) = delete; + Sqlite3Transaction &operator=(Sqlite3Transaction &&rhs) + { if (this != &rhs) { Commit(); std::swap(m_db, rhs.m_db); @@ -124,11 +142,13 @@ namespace { } } - explicit operator bool() const { + explicit operator bool() const + { return m_db != nullptr; } - void Rollback() { + void Rollback() + { if (m_db) { Exec(m_db, *m_callback, u8"ROLLBACK"); m_db = nullptr; @@ -136,7 +156,8 @@ namespace { } } - bool Commit() { + bool Commit() + { if (!m_db) { return false; } @@ -146,13 +167,15 @@ namespace { return result; } - ~Sqlite3Transaction() { + ~Sqlite3Transaction() + { Rollback(); } }; // Appends argcount variables to prefix in a comma-separated list. - std::string MakeSQLiteParameterizedStatement(const char* prefix, int argCount) { + std::string MakeSQLiteParameterizedStatement(const char *prefix, int argCount) + { assert(argCount != 0); std::string result(prefix); result.reserve(result.size() + (argCount * 2) + 1); @@ -166,11 +189,11 @@ namespace { // Checks if sqliteResult is SQLITE_OK. If not, reports the error via // callback & returns false. - bool CheckSQLiteResult(sqlite3* db, const DBStorage::Callback& callback, int sqliteResult) { + bool CheckSQLiteResult(sqlite3 *db, const DBStorage::Callback &callback, int sqliteResult) + { if (sqliteResult == SQLITE_OK) { return true; - } - else { + } else { InvokeError(callback, sqlite3_errmsg(db)); return false; } @@ -179,28 +202,28 @@ namespace { using Statement = std::unique_ptr; // Creates a prepared SQLite statement. On error, returns nullptr - Statement PrepareStatement(sqlite3* db, DBStorage::Callback& callback, const char* stmt) { - sqlite3_stmt* pStmt{ nullptr }; + Statement PrepareStatement(sqlite3 *db, DBStorage::Callback &callback, const char *stmt) + { + sqlite3_stmt *pStmt{nullptr}; if (!CheckSQLiteResult(db, callback, sqlite3_prepare_v2(db, stmt, -1, &pStmt, nullptr))) { - return { nullptr, sqlite3_finalize }; + return {nullptr, sqlite3_finalize}; } - return { pStmt, &sqlite3_finalize }; + return {pStmt, &sqlite3_finalize}; } // Binds the index-th variable in this prepared statement to str. - bool BindString( - sqlite3* db, - const DBStorage::Callback& callback, - const Statement& stmt, - int index, - const std::string& str) { - return CheckSQLiteResult(db, callback, sqlite3_bind_text(stmt.get(), index, str.c_str(), -1, SQLITE_TRANSIENT)); + bool BindString(sqlite3 *db, + const DBStorage::Callback &callback, + const Statement &stmt, + int index, + const std::string &str) + { + return CheckSQLiteResult( + db, callback, sqlite3_bind_text(stmt.get(), index, str.c_str(), -1, SQLITE_TRANSIENT)); } - struct slim_shared_lock_guard - { - explicit slim_shared_lock_guard(winrt::slim_mutex& m) noexcept : - m_mutex(m) + struct slim_shared_lock_guard { + explicit slim_shared_lock_guard(winrt::slim_mutex &m) noexcept : m_mutex(m) { m_mutex.lock_shared(); } @@ -211,65 +234,67 @@ namespace { } private: - winrt::slim_mutex& m_mutex; + winrt::slim_mutex &m_mutex; }; -} // namespace +} // namespace -DBStorage::DBStorage() { +DBStorage::DBStorage() +{ std::string path; if (auto pathInspectable = winrt::CoreApplication::Properties().TryLookup(s_dbPathProperty)) { auto pathHstring = winrt::unbox_value(pathInspectable); path = ConvertWstrToStr(std::wstring(pathHstring.c_str())); - } - else { + } else { try { auto const localAppDataPath = winrt::ApplicationData::Current().LocalFolder().Path(); std::wstring wPath(localAppDataPath.data()); wPath += L"\\AsyncStorage.db"; path = ConvertWstrToStr(wPath); - } - catch (winrt::hresult_error const&) { - throw std::runtime_error("Please specify 'React-Native-Community-Async-Storage-Database-Path' in CoreApplication::Properties"); + } catch (winrt::hresult_error const &) { + throw std::runtime_error( + "Please specify 'React-Native-Community-Async-Storage-Database-Path' in " + "CoreApplication::Properties"); } } - if (sqlite3_open_v2( - path.c_str(), - &m_db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, - nullptr) != SQLITE_OK) { + if (sqlite3_open_v2(path.c_str(), + &m_db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, + nullptr) != SQLITE_OK) { auto exception = std::runtime_error(sqlite3_errmsg(m_db)); sqlite3_close(m_db); throw exception; } int userVersion = 0; - auto getUserVersionCallback = [](void* pv, int cCol, char** rgszColText, char** /*rgszColName*/) { - if (cCol < 1) { - return 1; - } - *static_cast(pv) = atoi(rgszColText[0]); - return SQLITE_OK; - }; + auto getUserVersionCallback = + [](void *pv, int cCol, char **rgszColText, char ** /*rgszColName*/) { + if (cCol < 1) { + return 1; + } + *static_cast(pv) = atoi(rgszColText[0]); + return SQLITE_OK; + }; Exec(m_db, u8"PRAGMA user_version", getUserVersionCallback, &userVersion); if (userVersion == 0) { - Exec( - m_db, - u8"CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT NOT NULL); PRAGMA user_version=1"); + Exec(m_db, + u8"CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT NOT " + u8"NULL); PRAGMA user_version=1"); } } -DBStorage::~DBStorage() { +DBStorage::~DBStorage() +{ decltype(m_tasks) tasks; { // If there is an in-progress async task, cancel it and wait on the // condition_variable for the async task to acknowledge cancellation by // nulling out m_action. Once m_action is null, it is safe to proceed // wth closing the DB connection - slim_shared_lock_guard guard{ m_lock }; + slim_shared_lock_guard guard{m_lock}; swap(tasks, m_tasks); if (m_action) { m_action.Cancel(); @@ -279,9 +304,12 @@ DBStorage::~DBStorage() { sqlite3_close(m_db); } -std::string DBStorage::ConvertWstrToStr(const std::wstring& wstr) +std::string DBStorage::ConvertWstrToStr(const std::wstring &wstr) { - if (wstr.empty()) return std::string(); + if (wstr.empty()) { + return std::string(); + } + int size = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string str(size, 0); WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &str[0], size, NULL, NULL); @@ -290,10 +318,10 @@ std::string DBStorage::ConvertWstrToStr(const std::wstring& wstr) // Under the lock, add a task to m_tasks and, if no async task is in progress, // schedule it -void DBStorage::AddTask( - DBStorage::DBTask::Type type, - std::vector&& args, - DBStorage::Callback&& jsCallback) { +void DBStorage::AddTask(DBStorage::DBTask::Type type, + std::vector &&args, + DBStorage::Callback &&jsCallback) +{ winrt::slim_lock_guard guard(m_lock); m_tasks.emplace_back(type, std::move(args), std::move(jsCallback)); if (!m_action) { @@ -308,12 +336,13 @@ void DBStorage::AddTask( // m_tasks is empty and acknowledging completion is done atomically; otherwise // there would be a race between the background task detecting m_tasks.empty() // and AddTask checking the coroutine is running. -winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() { +winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() +{ auto cancellationToken = co_await winrt::get_cancellation_token(); co_await winrt::resume_background(); while (!cancellationToken()) { decltype(m_tasks) tasks; - sqlite3* db{ nullptr }; + sqlite3 *db{nullptr}; { winrt::slim_lock_guard guard(m_lock); if (m_tasks.empty()) { @@ -325,7 +354,7 @@ winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() { db = m_db; } - for (auto& task : tasks) { + for (auto &task : tasks) { task.Run(db); if (cancellationToken()) break; @@ -336,33 +365,36 @@ winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() { m_cv.notify_all(); } -void DBStorage::DBTask::Run(sqlite3* db) { +void DBStorage::DBTask::Run(sqlite3 *db) +{ switch (m_type) { - case Type::multiGet: - multiGet(db); - break; - case Type::multiSet: - multiSet(db); - break; - case Type::multiRemove: - multiRemove(db); - break; - case Type::clear: - clear(db); - break; - case Type::getAllKeys: - getAllKeys(db); - break; + case Type::multiGet: + multiGet(db); + break; + case Type::multiSet: + multiSet(db); + break; + case Type::multiRemove: + multiRemove(db); + break; + case Type::clear: + clear(db); + break; + case Type::getAllKeys: + getAllKeys(db); + break; } } -void DBStorage::DBTask::multiGet(sqlite3* db) { +void DBStorage::DBTask::multiGet(sqlite3 *db) +{ if (!CheckArgs(db, m_args, m_callback)) { return; } auto argCount = static_cast(m_args.size()); - auto sql = MakeSQLiteParameterizedStatement(u8"SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount); + auto sql = MakeSQLiteParameterizedStatement( + u8"SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount); auto pStmt = PrepareStatement(db, m_callback, sql.data()); if (!pStmt) { return; @@ -373,23 +405,24 @@ void DBStorage::DBTask::multiGet(sqlite3* db) { } auto result = winrt::JSValueArray{}; - for (auto stepResult = sqlite3_step(pStmt.get()); stepResult != SQLITE_DONE; stepResult = sqlite3_step(pStmt.get())) { + for (auto stepResult = sqlite3_step(pStmt.get()); stepResult != SQLITE_DONE; + stepResult = sqlite3_step(pStmt.get())) { if (stepResult != SQLITE_ROW) { InvokeError(m_callback, sqlite3_errmsg(db)); return; } - auto key = reinterpret_cast(sqlite3_column_text(pStmt.get(), 0)); + auto key = reinterpret_cast(sqlite3_column_text(pStmt.get(), 0)); if (!key) { InvokeError(m_callback, sqlite3_errmsg(db)); return; } - auto value = reinterpret_cast(sqlite3_column_text(pStmt.get(), 1)); + auto value = reinterpret_cast(sqlite3_column_text(pStmt.get(), 1)); if (!value) { InvokeError(m_callback, sqlite3_errmsg(db)); return; } - result.push_back(winrt::JSValueArray({ key, value })); + result.push_back(winrt::JSValueArray({key, value})); } auto writer = winrt::MakeJSValueTreeWriter(); result.WriteTo(writer); @@ -399,16 +432,18 @@ void DBStorage::DBTask::multiGet(sqlite3* db) { m_callback(callbackParams); } -void DBStorage::DBTask::multiSet(sqlite3* db) { +void DBStorage::DBTask::multiSet(sqlite3 *db) +{ Sqlite3Transaction transaction(db, m_callback); if (!transaction) { return; } - auto pStmt = PrepareStatement(db, m_callback, u8"INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)"); + auto pStmt = + PrepareStatement(db, m_callback, u8"INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)"); if (!pStmt) { return; } - for (auto&& arg : m_args) { + for (auto &&arg : m_args) { if (!BindString(db, m_callback, pStmt, 1, arg[0].AsString()) || !BindString(db, m_callback, pStmt, 2, arg[1].AsString())) { return; @@ -429,23 +464,26 @@ void DBStorage::DBTask::multiSet(sqlite3* db) { m_callback(callbackParams); } -void DBStorage::DBTask::multiRemove(sqlite3* db) { +void DBStorage::DBTask::multiRemove(sqlite3 *db) +{ if (!CheckArgs(db, m_args, m_callback)) { return; } auto argCount = static_cast(m_args.size()); - auto sql = MakeSQLiteParameterizedStatement(u8"DELETE FROM AsyncLocalStorage WHERE key IN ", argCount); + auto sql = + MakeSQLiteParameterizedStatement(u8"DELETE FROM AsyncLocalStorage WHERE key IN ", argCount); auto pStmt = PrepareStatement(db, m_callback, sql.data()); if (!pStmt) { return; } for (int i = 0; i < argCount; i++) { - if (!BindString(db, m_callback, pStmt, i+1, m_args[i].AsString())) { + if (!BindString(db, m_callback, pStmt, i + 1, m_args[i].AsString())) { return; } } - for (auto stepResult = sqlite3_step(pStmt.get()); stepResult != SQLITE_DONE; stepResult = sqlite3_step(pStmt.get())) { + for (auto stepResult = sqlite3_step(pStmt.get()); stepResult != SQLITE_DONE; + stepResult = sqlite3_step(pStmt.get())) { if (stepResult != SQLITE_ROW) { InvokeError(m_callback, sqlite3_errmsg(db)); return; @@ -456,9 +494,10 @@ void DBStorage::DBTask::multiRemove(sqlite3* db) { m_callback(callbackParams); } -void DBStorage::DBTask::getAllKeys(sqlite3* db) { +void DBStorage::DBTask::getAllKeys(sqlite3 *db) +{ winrt::JSValueArray result; - auto getAllKeysCallback = [&](int cCol, char** rgszColText, char**) { + auto getAllKeysCallback = [&](int cCol, char **rgszColText, char **) { if (cCol >= 1) { result.push_back(rgszColText[0]); } @@ -475,7 +514,8 @@ void DBStorage::DBTask::getAllKeys(sqlite3* db) { } } -void DBStorage::DBTask::clear(sqlite3* db) { +void DBStorage::DBTask::clear(sqlite3 *db) +{ if (Exec(db, m_callback, u8"DELETE FROM AsyncLocalStorage")) { std::vector callbackParams; callbackParams.push_back(nullptr); diff --git a/windows/ReactNativeAsyncStorage/DBStorage.h b/windows/ReactNativeAsyncStorage/DBStorage.h index 8df4f330..7d402e07 100644 --- a/windows/ReactNativeAsyncStorage/DBStorage.h +++ b/windows/ReactNativeAsyncStorage/DBStorage.h @@ -3,52 +3,69 @@ #pragma once #include + #include "NativeModules.h" -class DBStorage { +class DBStorage +{ public: - typedef std::function const&)> Callback; + typedef std::function const &)> + Callback; - class DBTask { + class DBTask + { public: enum class Type { multiGet, multiSet, multiRemove, clear, getAllKeys }; - DBTask(Type type, std::vector&& args, - Callback&& callback) - : m_type{ type }, m_args{ std::move(args) }, m_callback{ std::move(callback) } {} - DBTask(const DBTask&) = delete; - DBTask(DBTask&&) = default; - DBTask& operator=(const DBTask&) = delete; - DBTask& operator=(DBTask&&) = default; - void Run(sqlite3* db); + + DBTask(Type type, + std::vector &&args, + Callback &&callback) + : m_type{type}, m_args{std::move(args)}, m_callback{std::move(callback)} + { + } + + DBTask(const DBTask &) = delete; + DBTask(DBTask &&) = default; + DBTask &operator=(const DBTask &) = delete; + DBTask &operator=(DBTask &&) = default; + void Run(sqlite3 *db); private: Type m_type; std::vector m_args; Callback m_callback; - void multiGet(sqlite3* db); - void multiSet(sqlite3* db); - void multiRemove(sqlite3* db); - void clear(sqlite3* db); - void getAllKeys(sqlite3* db); + void multiGet(sqlite3 *db); + void multiSet(sqlite3 *db); + void multiRemove(sqlite3 *db); + void clear(sqlite3 *db); + void getAllKeys(sqlite3 *db); }; - DBStorage(); - ~DBStorage(); + DBStorage(); + ~DBStorage(); + + void AddTask(DBTask::Type type, + std::vector &&args, + Callback &&jsCallback); - void AddTask(DBTask::Type type, std::vector&& args, Callback&& jsCallback); - void AddTask(DBTask::Type type, Callback&& jsCallback) { - AddTask(type, std::move(std::vector()), std::move(jsCallback)); + void AddTask(DBTask::Type type, Callback &&jsCallback) + { + AddTask(type, + std::move(std::vector()), + std::move(jsCallback)); } + winrt::Windows::Foundation::IAsyncAction RunTasks(); private: - sqlite3* m_db; + static constexpr auto s_dbPathProperty = L"React-Native-Community-Async-Storage-Database-Path"; + + sqlite3 *m_db; winrt::slim_mutex m_lock; winrt::slim_condition_variable m_cv; - winrt::Windows::Foundation::IAsyncAction m_action{ nullptr }; + winrt::Windows::Foundation::IAsyncAction m_action{nullptr}; std::vector m_tasks; - static constexpr auto s_dbPathProperty = L"React-Native-Community-Async-Storage-Database-Path"; - std::string ConvertWstrToStr(const std::wstring& wstr); + std::string ConvertWstrToStr(const std::wstring &wstr); }; diff --git a/windows/ReactNativeAsyncStorage/RNCAsyncStorage.h b/windows/ReactNativeAsyncStorage/RNCAsyncStorage.h index eba01af7..d784c23e 100644 --- a/windows/ReactNativeAsyncStorage/RNCAsyncStorage.h +++ b/windows/ReactNativeAsyncStorage/RNCAsyncStorage.h @@ -3,26 +3,30 @@ #pragma once #include "pch.h" -#include "NativeModules.h" + #include "DBStorage.h" +#include "NativeModules.h" namespace winrt::ReactNativeAsyncStorage::implementation { REACT_MODULE(RNCAsyncStorage); - struct RNCAsyncStorage - { + struct RNCAsyncStorage { DBStorage dbStorage; REACT_METHOD(multiGet); - void multiGet(std::vector keys, std::function&& callback) noexcept { - dbStorage.AddTask(DBStorage::DBTask::Type::multiGet, std::move(keys), - [callback{ std::move(callback) }](std::vector const& callbackParams) { + void multiGet(std::vector keys, + std::function + &&callback) noexcept + { + dbStorage.AddTask( + DBStorage::DBTask::Type::multiGet, + std::move(keys), + [callback{std::move(callback)}](std::vector const &callbackParams) { if (callbackParams.size() > 0) { - auto& errors = callbackParams[0].AsArray(); + auto &errors = callbackParams[0].AsArray(); if (callbackParams.size() > 1) { callback(errors, callbackParams[1].AsArray()); - } - else { + } else { callback(errors, {}); } } @@ -30,90 +34,107 @@ namespace winrt::ReactNativeAsyncStorage::implementation } REACT_METHOD(multiSet); - void multiSet(std::vector pairs, std::function&& callback) noexcept { - dbStorage.AddTask(DBStorage::DBTask::Type::multiSet, std::move(pairs), - [callback{ std::move(callback) }](std::vector const& callbackParams) { + void multiSet(std::vector pairs, + std::function &&callback) noexcept + { + dbStorage.AddTask( + DBStorage::DBTask::Type::multiSet, + std::move(pairs), + [callback{std::move(callback)}](std::vector const &callbackParams) { if (callbackParams.size() > 0) { - auto& errors = callbackParams[0].AsArray(); + auto &errors = callbackParams[0].AsArray(); callback(errors); } }); } REACT_METHOD(multiMerge); - void multiMerge(std::vector pairs, std::function&& callback) noexcept { + void multiMerge(std::vector pairs, + std::function &&callback) noexcept + { std::vector keys; std::vector newValues; - for (const auto& pair : pairs) { + for (const auto &pair : pairs) { keys.push_back(pair.AsArray()[0].AsString()); newValues.push_back(pair.AsArray()[1].AsString()); } - multiGet(std::move(keys), [newValues{ std::move(newValues) }, callback{ std::move(callback) }, this](JSValueArray const& errors, JSValueArray const& results) { - if (errors.size() > 0) { - callback(errors); - return; - } + multiGet(std::move(keys), + [newValues{std::move(newValues)}, callback{std::move(callback)}, this]( + JSValueArray const &errors, JSValueArray const &results) { + if (errors.size() > 0) { + callback(errors); + return; + } - std::vector mergedResults; - - for (int i = 0; i < results.size(); i++) { - auto& oldPair = results[i].AsArray(); - auto& key = oldPair[0]; - auto oldValue = oldPair[1].AsString(); - auto& newValue = newValues[i]; - - winrt::Windows::Data::Json::JsonObject oldJson; - winrt::Windows::Data::Json::JsonObject newJson; - if (winrt::Windows::Data::Json::JsonObject::TryParse(winrt::to_hstring(oldValue), oldJson) - && winrt::Windows::Data::Json::JsonObject::TryParse(winrt::to_hstring(newValue), newJson)) { - MergeJsonObjects(oldJson, newJson); - - JSValue value; - auto writer = MakeJSValueTreeWriter(); - writer.WriteArrayBegin(); - WriteValue(writer, key); - WriteValue(writer, oldJson.ToString()); - writer.WriteArrayEnd(); - mergedResults.push_back(TakeJSValue(writer)); - } - else { - auto writer = MakeJSValueTreeWriter(); - writer.WriteObjectBegin(); - WriteProperty(writer, L"message", L"Values must be valid Json strings"); - writer.WriteObjectEnd(); - callback(JSValueArray{ TakeJSValue(writer) }); - return; - } - } + std::vector mergedResults; - multiSet(std::move(mergedResults), [callback{ std::move(callback) }](JSValueArray const& errors) { - callback(errors); - }); - }); + for (int i = 0; i < results.size(); i++) { + auto &oldPair = results[i].AsArray(); + auto &key = oldPair[0]; + auto oldValue = oldPair[1].AsString(); + auto &newValue = newValues[i]; + + winrt::Windows::Data::Json::JsonObject oldJson; + winrt::Windows::Data::Json::JsonObject newJson; + if (winrt::Windows::Data::Json::JsonObject::TryParse( + winrt::to_hstring(oldValue), oldJson) && + winrt::Windows::Data::Json::JsonObject::TryParse( + winrt::to_hstring(newValue), newJson)) { + MergeJsonObjects(oldJson, newJson); + + JSValue value; + auto writer = MakeJSValueTreeWriter(); + writer.WriteArrayBegin(); + WriteValue(writer, key); + WriteValue(writer, oldJson.ToString()); + writer.WriteArrayEnd(); + mergedResults.push_back(TakeJSValue(writer)); + } else { + auto writer = MakeJSValueTreeWriter(); + writer.WriteObjectBegin(); + WriteProperty( + writer, L"message", L"Values must be valid Json strings"); + writer.WriteObjectEnd(); + callback(JSValueArray{TakeJSValue(writer)}); + return; + } + } + + multiSet(std::move(mergedResults), + [callback{std::move(callback)}](JSValueArray const &errors) { + callback(errors); + }); + }); } REACT_METHOD(multiRemove); - void multiRemove(std::vector keys, std::function&& callback) noexcept { - dbStorage.AddTask(DBStorage::DBTask::Type::multiRemove, std::move(keys), - [callback{ std::move(callback) }](std::vector const& callbackParams) { + void multiRemove(std::vector keys, + std::function &&callback) noexcept + { + dbStorage.AddTask( + DBStorage::DBTask::Type::multiRemove, + std::move(keys), + [callback{std::move(callback)}](std::vector const &callbackParams) { if (callbackParams.size() > 0) { - auto& errors = callbackParams[0].AsArray(); + auto &errors = callbackParams[0].AsArray(); callback(errors); } }); } REACT_METHOD(getAllKeys); - void getAllKeys(std::function&& callback) noexcept { - dbStorage.AddTask(DBStorage::DBTask::Type::getAllKeys, - [callback{ std::move(callback) }](std::vector const& callbackParams) { + void getAllKeys( + std::function &&callback) noexcept + { + dbStorage.AddTask( + DBStorage::DBTask::Type::getAllKeys, + [callback{std::move(callback)}](std::vector const &callbackParams) { if (callbackParams.size() > 0) { - auto& error = callbackParams[0]; + auto &error = callbackParams[0]; if (callbackParams.size() > 1) { callback(error, callbackParams[1].AsArray()); - } - else { + } else { callback(error, {}); } } @@ -121,31 +142,34 @@ namespace winrt::ReactNativeAsyncStorage::implementation } REACT_METHOD(clear); - void clear(std::function&& callback) noexcept { - dbStorage.AddTask(DBStorage::DBTask::Type::clear, - [callback{ std::move(callback) }](std::vector const& callbackParams) { + void clear(std::function &&callback) noexcept + { + dbStorage.AddTask( + DBStorage::DBTask::Type::clear, + [callback{std::move(callback)}](std::vector const &callbackParams) { if (callbackParams.size() > 0) { - auto& error = callbackParams[0]; + auto &error = callbackParams[0]; callback(error); } }); } // Merge newJson into oldJson - void MergeJsonObjects(winrt::Windows::Data::Json::JsonObject const& oldJson, winrt::Windows::Data::Json::JsonObject const& newJson) { + void MergeJsonObjects(winrt::Windows::Data::Json::JsonObject const &oldJson, + winrt::Windows::Data::Json::JsonObject const &newJson) + { for (auto pair : newJson) { auto key = pair.Key(); auto newValue = pair.Value(); - if (newValue.ValueType() == winrt::Windows::Data::Json::JsonValueType::Object - && oldJson.HasKey(key)) { + if (newValue.ValueType() == winrt::Windows::Data::Json::JsonValueType::Object && + oldJson.HasKey(key)) { auto oldValue = oldJson.GetNamedObject(key); MergeJsonObjects(oldValue, newValue.GetObject()); oldJson.SetNamedValue(key, oldValue); - } - else { + } else { oldJson.SetNamedValue(key, newValue); } } } }; -} \ No newline at end of file +} // namespace winrt::ReactNativeAsyncStorage::implementation diff --git a/windows/ReactNativeAsyncStorage/ReactPackageProvider.cpp b/windows/ReactNativeAsyncStorage/ReactPackageProvider.cpp index 13d2a8dc..ae1f5a82 100644 --- a/windows/ReactNativeAsyncStorage/ReactPackageProvider.cpp +++ b/windows/ReactNativeAsyncStorage/ReactPackageProvider.cpp @@ -1,16 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" + #include "ReactPackageProvider.h" -#include "ReactPackageProvider.g.cpp" + #include "RNCAsyncStorage.h" +#include "ReactPackageProvider.g.cpp" using namespace winrt::Microsoft::ReactNative; -namespace winrt::ReactNativeAsyncStorage::implementation { +namespace winrt::ReactNativeAsyncStorage::implementation +{ - void ReactPackageProvider::CreatePackage(IReactPackageBuilder const& packageBuilder) noexcept { + void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept + { AddAttributedModules(packageBuilder); } -} // namespace winrt::ReactNativeAsyncStorage::implementation +} // namespace winrt::ReactNativeAsyncStorage::implementation diff --git a/windows/ReactNativeAsyncStorage/ReactPackageProvider.h b/windows/ReactNativeAsyncStorage/ReactPackageProvider.h index 3832874e..b7dbc017 100644 --- a/windows/ReactNativeAsyncStorage/ReactPackageProvider.h +++ b/windows/ReactNativeAsyncStorage/ReactPackageProvider.h @@ -1,22 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once + #include "ReactPackageProvider.g.h" using namespace winrt::Microsoft::ReactNative; -namespace winrt::ReactNativeAsyncStorage::implementation { - +namespace winrt::ReactNativeAsyncStorage::implementation +{ struct ReactPackageProvider : ReactPackageProviderT { ReactPackageProvider() = default; - void CreatePackage(IReactPackageBuilder const& packageBuilder) noexcept; + void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; }; +} // namespace winrt::ReactNativeAsyncStorage::implementation -} // namespace winrt::ReactNativeAsyncStorage::implementation - -namespace winrt::ReactNativeAsyncStorage::factory_implementation { - - struct ReactPackageProvider : ReactPackageProviderT {}; - -} // namespace winrt::ReactNativeAsyncStorage::factory_implementation \ No newline at end of file +namespace winrt::ReactNativeAsyncStorage::factory_implementation +{ + struct ReactPackageProvider + : ReactPackageProviderT { + }; +} // namespace winrt::ReactNativeAsyncStorage::factory_implementation diff --git a/windows/ReactNativeAsyncStorage/pch.h b/windows/ReactNativeAsyncStorage/pch.h index a048c7ac..1bf564d0 100644 --- a/windows/ReactNativeAsyncStorage/pch.h +++ b/windows/ReactNativeAsyncStorage/pch.h @@ -4,11 +4,12 @@ #define NOMINMAX -#include #include +#include + +#include #include #include -#include #include +#include #include -#include