diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..59c04f27 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: Google +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 0c01a008..35084548 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -26,7 +26,7 @@ jobs: cache: "npm" - run: corepack enable - run: yarn - - run: yarn lint + - run: yarn lint:js - run: yarn test --watchAll=false --coverage - run: yarn build - uses: codecov/codecov-action@v4 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..965b1094 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +yarn run lint-staged \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..f03ce7fe --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,49 @@ +included: + - packages + - integration-tests + - examples + +excluded: + - Pods + - node_modules + +disabled_rules: # rule identifiers turned on by default to exclude from running + - identifier_name + - function_parameter_count + - notification_center_detachment + - compiler_protocol_init + +opt_in_rules: + - conditional_returns_on_newline + +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging, summary) + +file_length: + warning: 800 + error: 1000 + +line_length: + ignores_comments: true + ignores_interpolated_strings: true + +type_body_length: + warning: 600 + error: 800 + +type_name: + max_length: + warning: 60 + allowed_symbols: ["_"] + +function_body_length: + warning: 100 + error: 300 + +large_tuple: + warning: 4 + error: 6 + +cyclomatic_complexity: + ignores_case_statements: true + warning: 20 + error: 30 diff --git a/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationModuleProvider.h b/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationModuleProvider.h index 0fa43fa6..f246b99f 100644 --- a/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationModuleProvider.h +++ b/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationModuleProvider.h @@ -1,16 +1,15 @@ #pragma once +#include + #include #include -#include - namespace facebook { namespace react { std::shared_ptr MainApplicationModuleProvider( - const std::string moduleName, - const JavaTurboModule::InitParams ¶ms); + const std::string moduleName, const JavaTurboModule::InitParams ¶ms); -} // namespace react -} // namespace facebook +} // namespace react +} // namespace facebook diff --git a/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h b/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h index bb75d7ce..750ddf25 100644 --- a/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h +++ b/examples/react-native-test-suite/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h @@ -1,38 +1,39 @@ -#include -#include - #include #include +#include +#include + namespace facebook { namespace react { class MainApplicationTurboModuleManagerDelegate - : public jni::HybridClass< - MainApplicationTurboModuleManagerDelegate, - TurboModuleManagerDelegate> { - public: - // Adapt it to the package you used for your Java class. - static constexpr auto kJavaDescriptor = - "Lcom/embracetestsuite/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;"; - - static jni::local_ref initHybrid(jni::alias_ref); - - static void registerNatives(); - - std::shared_ptr getTurboModule( - const std::string name, - const std::shared_ptr jsInvoker) override; - std::shared_ptr getTurboModule( - const std::string name, - const JavaTurboModule::InitParams ¶ms) override; - - /** - * Test-only method. Allows user to verify whether a TurboModule can be - * created by instances of this class. - */ - bool canCreateTurboModule(std::string name); + : public jni::HybridClass { + public: + // Adapt it to the package you used for your Java class. + static constexpr auto kJavaDescriptor = + "Lcom/embracetestsuite/newarchitecture/modules/" + "MainApplicationTurboModuleManagerDelegate;"; + + static jni::local_ref initHybrid( + jni::alias_ref); + + static void registerNatives(); + + std::shared_ptr getTurboModule( + const std::string name, + const std::shared_ptr jsInvoker) override; + std::shared_ptr getTurboModule( + const std::string name, + const JavaTurboModule::InitParams ¶ms) override; + + /** + * Test-only method. Allows user to verify whether a TurboModule can be + * created by instances of this class. + */ + bool canCreateTurboModule(std::string name); }; -} // namespace react -} // namespace facebook +} // namespace react +} // namespace facebook diff --git a/examples/react-native-test-suite/android/app/src/main/jni/MainComponentsRegistry.h b/examples/react-native-test-suite/android/app/src/main/jni/MainComponentsRegistry.h index 7ec90390..4ca8d91b 100644 --- a/examples/react-native-test-suite/android/app/src/main/jni/MainComponentsRegistry.h +++ b/examples/react-native-test-suite/android/app/src/main/jni/MainComponentsRegistry.h @@ -10,23 +10,23 @@ namespace react { class MainComponentsRegistry : public facebook::jni::HybridClass { - public: - // Adapt it to the package you used for your Java class. - constexpr static auto kJavaDescriptor = - "Lcom/embracetestsuite/newarchitecture/components/MainComponentsRegistry;"; + public: + // Adapt it to the package you used for your Java class. + constexpr static auto kJavaDescriptor = + "Lcom/embracetestsuite/newarchitecture/components/" + "MainComponentsRegistry;"; - static void registerNatives(); + static void registerNatives(); - MainComponentsRegistry(ComponentFactory *delegate); + MainComponentsRegistry(ComponentFactory *delegate); - private: - static std::shared_ptr - sharedProviderRegistry(); + private: + static std::shared_ptr + sharedProviderRegistry(); - static jni::local_ref initHybrid( - jni::alias_ref, - ComponentFactory *delegate); + static jni::local_ref initHybrid(jni::alias_ref, + ComponentFactory *delegate); }; -} // namespace react -} // namespace facebook +} // namespace react +} // namespace facebook diff --git a/examples/react-native-test-suite/ios/CRLCrash.h b/examples/react-native-test-suite/ios/CRLCrash.h index ef640cb1..0c5fdba7 100644 --- a/examples/react-native-test-suite/ios/CRLCrash.h +++ b/examples/react-native-test-suite/ios/CRLCrash.h @@ -6,9 +6,9 @@ + (void)registerCrash:(CRLCrash *)crash; + (void)unregisterCrash:(CRLCrash *)crash; -@property(nonatomic,copy,readonly) NSString *category; -@property(nonatomic,copy,readonly) NSString *title; -@property(nonatomic,copy,readonly) NSString *desc; +@property(nonatomic, copy, readonly) NSString *category; +@property(nonatomic, copy, readonly) NSString *title; +@property(nonatomic, copy, readonly) NSString *desc; - (void)crash; diff --git a/examples/react-native-test-suite/ios/CRLCrash.m b/examples/react-native-test-suite/ios/CRLCrash.m index 0bee3467..c55d3c14 100644 --- a/examples/react-native-test-suite/ios/CRLCrash.m +++ b/examples/react-native-test-suite/ios/CRLCrash.m @@ -5,46 +5,40 @@ @implementation CRLCrash -+ (void)initialize -{ - static dispatch_once_t predicate = 0; - - dispatch_once(&predicate, ^ { crashTypes = [[NSMutableSet alloc] init]; }); ++ (void)initialize { + static dispatch_once_t predicate = 0; + + dispatch_once(&predicate, ^{ + crashTypes = [[NSMutableSet alloc] init]; + }); } -+ (NSArray *)allCrashes -{ - return crashTypes.allObjects; ++ (NSArray *)allCrashes { + return crashTypes.allObjects; } -+ (void)registerCrash:(CRLCrash *)crash -{ - [crashTypes addObject:crash]; ++ (void)registerCrash:(CRLCrash *)crash { + [crashTypes addObject:crash]; } -+ (void)unregisterCrash:(CRLCrash *)crash -{ - [crashTypes removeObject:crash]; ++ (void)unregisterCrash:(CRLCrash *)crash { + [crashTypes removeObject:crash]; } -- (NSString *)category -{ - return @"NONE"; +- (NSString *)category { + return @"NONE"; } -- (NSString *)title -{ - return @"NONE"; +- (NSString *)title { + return @"NONE"; } -- (NSString *)desc -{ - return @"NONE"; +- (NSString *)desc { + return @"NONE"; } -- (void)crash -{ - NSLog(@"I'm supposed to crash here."); +- (void)crash { + NSLog(@"I'm supposed to crash here."); } @end diff --git a/examples/react-native-test-suite/ios/CRLCrashCXXException.mm b/examples/react-native-test-suite/ios/CRLCrashCXXException.mm index f4fe0edb..3ee20bff 100644 --- a/examples/react-native-test-suite/ios/CRLCrashCXXException.mm +++ b/examples/react-native-test-suite/ios/CRLCrashCXXException.mm @@ -2,29 +2,31 @@ #import "CRLCrashCXXException.h" #import -class kaboom_exception : public std::exception -{ - virtual const char* what() const throw(); +class kaboom_exception : public std::exception { + virtual const char *what() const throw(); }; -const char* kaboom_exception::what() const throw() -{ - return "If this had been a real exception, you would be cursing now."; +const char *kaboom_exception::what() const throw() { + return "If this had been a real exception, you would be cursing now."; } @implementation CRLCrashCXXException -- (NSString *)category { return @"Exceptions"; } -- (NSString *)title { return @"Throw C++ exception"; } -- (NSString *)desc { return @"" - "Throw an uncaught C++ exception. " - "This is a difficult case for crash reporters to handle, " - "as it involves the destruction of the data necessary to generate a correct backtrace."; +- (NSString *)category { + return @"Exceptions"; +} +- (NSString *)title { + return @"Throw C++ exception"; +} +- (NSString *)desc { + return @"" + "Throw an uncaught C++ exception. " + "This is a difficult case for crash reporters to handle, " + "as it involves the destruction of the data necessary to generate a correct backtrace."; } -- (void)crash __attribute__((noreturn)) -{ - throw new kaboom_exception; +- (void)crash __attribute__((noreturn)) { + throw new kaboom_exception; } @end diff --git a/examples/react-native-test-suite/ios/EmbraceTestSuite/AppDelegate.mm b/examples/react-native-test-suite/ios/EmbraceTestSuite/AppDelegate.mm index 9ea88adf..abccf7b9 100644 --- a/examples/react-native-test-suite/ios/EmbraceTestSuite/AppDelegate.mm +++ b/examples/react-native-test-suite/ios/EmbraceTestSuite/AppDelegate.mm @@ -1,38 +1,35 @@ #import "AppDelegate.h" -#import #import +#import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - options:(NSDictionary *)options -{ - return [RCTLinkingManager application:application openURL:url options:options]; + openURL:(NSURL *)url + options:(NSDictionary *)options { + return [RCTLinkingManager application:application openURL:url options:options]; } -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - - - [[Embrace sharedInstance] startWithLaunchOptions:launchOptions framework:EMBAppFrameworkReactNative]; - - self.moduleName = @"EmbraceTestSuite"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [[Embrace sharedInstance] startWithLaunchOptions:launchOptions + framework:EMBAppFrameworkReactNative]; + + self.moduleName = @"EmbraceTestSuite"; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } diff --git a/examples/react-native-test-suite/ios/EmbraceTestSuite/main.m b/examples/react-native-test-suite/ios/EmbraceTestSuite/main.m index d645c724..841c545c 100644 --- a/examples/react-native-test-suite/ios/EmbraceTestSuite/main.m +++ b/examples/react-native-test-suite/ios/EmbraceTestSuite/main.m @@ -2,9 +2,8 @@ #import "AppDelegate.h" -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } } diff --git a/examples/react-native-test-suite/ios/EmbraceTestSuiteTests/EmbraceTestSuiteTests.m b/examples/react-native-test-suite/ios/EmbraceTestSuiteTests/EmbraceTestSuiteTests.m index bf49ab45..ace00ce5 100644 --- a/examples/react-native-test-suite/ios/EmbraceTestSuiteTests/EmbraceTestSuiteTests.m +++ b/examples/react-native-test-suite/ios/EmbraceTestSuiteTests/EmbraceTestSuiteTests.m @@ -13,54 +13,56 @@ @interface EmbraceTestSuiteTests : XCTestCase @implementation EmbraceTestSuiteTests -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test { + if (test(view)) { + return YES; } - } - return NO; + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; } -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; +- (void)testRendersWelcomeScreen { + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; - __block NSString *redboxError = nil; + __block NSString *redboxError = nil; #ifdef DEBUG - RCTSetLogFunction( - ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, + NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { redboxError = message; - } - }); + } + }); #endif - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes + beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - foundElement = [self findSubviewInView:vc.view - matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } + foundElement = + [self findSubviewInView:vc.view + matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } #ifdef DEBUG - RCTSetLogFunction(RCTDefaultLogFunction); + RCTSetLogFunction(RCTDefaultLogFunction); #endif - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", + TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); } @end diff --git a/examples/react-native-test-suite/ios/RCTNativeTestModule.mm b/examples/react-native-test-suite/ios/RCTNativeTestModule.mm index 5dec1ac9..18b817aa 100644 --- a/examples/react-native-test-suite/ios/RCTNativeTestModule.mm +++ b/examples/react-native-test-suite/ios/RCTNativeTestModule.mm @@ -5,34 +5,21 @@ @implementation RCTNativeTestModule RCT_EXPORT_MODULE(); -RCT_EXPORT_METHOD(generateNativeCrash) -{ - @throw NSInternalInconsistencyException; -} - +RCT_EXPORT_METHOD(generateNativeCrash) { @throw NSInternalInconsistencyException; } -RCT_EXPORT_METHOD(generatePlatformCrash) -{ - @throw [NSException exceptionWithName:@"NS Except exception" - reason:@"Test NS Exception" - userInfo:nil]; +RCT_EXPORT_METHOD(generatePlatformCrash) { + @throw [NSException exceptionWithName:@"NS Except exception" + reason:@"Test NS Exception" + userInfo:nil]; } -RCT_EXPORT_METHOD(generateCPPCrash) -{ - CRLCrashCXXException *crashInstance = [[CRLCrashCXXException alloc] init]; - [crashInstance crash]; +RCT_EXPORT_METHOD(generateCPPCrash) { + CRLCrashCXXException *crashInstance = [[CRLCrashCXXException alloc] init]; + [crashInstance crash]; } -RCT_EXPORT_METHOD(moveToBackground) -{ - [[UIApplication sharedApplication] performSelector:NSSelectorFromString(@"suspend")]; - +RCT_EXPORT_METHOD(moveToBackground) { + [[UIApplication sharedApplication] performSelector:NSSelectorFromString(@"suspend")]; } - - - - @end - diff --git a/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.h b/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.h index 1658a437..1f65766b 100644 --- a/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.h +++ b/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.h @@ -1,6 +1,6 @@ +#import #import #import -#import @interface AppDelegate : EXAppDelegateWrapper diff --git a/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.mm b/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.mm index b27f8328..64e3a0d3 100644 --- a/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.mm +++ b/integration-tests/basic-test-app/ios/basictestapp/AppDelegate.mm @@ -5,58 +5,75 @@ @implementation AppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"main"; +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + self.moduleName = @"main"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self bundleURL]; +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { + return [self bundleURL]; } -- (NSURL *)bundleURL -{ +- (NSURL *)bundleURL { #if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; + return [[RCTBundleURLProvider sharedSettings] + jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; #else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } // Linking API -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { - return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + options:(NSDictionary *)options { + return [super application:application openURL:url options:options] || + [RCTLinkingManager application:application openURL:url options:options]; } // Universal Links -- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { - BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; - return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; +- (BOOL)application:(UIApplication *)application + continueUserActivity:(nonnull NSUserActivity *)userActivity + restorationHandler: + (nonnull void (^)(NSArray> *_Nullable))restorationHandler { + BOOL result = [RCTLinkingManager application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]; + return [super application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler] || + result; } -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken -{ - return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +// Explicitly define remote notification delegates to ensure compatibility with some third-party +// libraries +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + return [super application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error -{ - return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; +// Explicitly define remote notification delegates to ensure compatibility with some third-party +// libraries +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; } -// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler -{ - return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; +// Explicitly define remote notification delegates to ensure compatibility with some third-party +// libraries +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + return [super application:application + didReceiveRemoteNotification:userInfo + fetchCompletionHandler:completionHandler]; } @end diff --git a/integration-tests/basic-test-app/ios/basictestapp/basictestapp-Bridging-Header.h b/integration-tests/basic-test-app/ios/basictestapp/basictestapp-Bridging-Header.h index e11d920b..339994e9 100644 --- a/integration-tests/basic-test-app/ios/basictestapp/basictestapp-Bridging-Header.h +++ b/integration-tests/basic-test-app/ios/basictestapp/basictestapp-Bridging-Header.h @@ -1,3 +1,4 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // diff --git a/integration-tests/basic-test-app/ios/basictestapp/main.m b/integration-tests/basic-test-app/ios/basictestapp/main.m index 25181b6c..420def2d 100644 --- a/integration-tests/basic-test-app/ios/basictestapp/main.m +++ b/integration-tests/basic-test-app/ios/basictestapp/main.m @@ -2,9 +2,8 @@ #import "AppDelegate.h" -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } } - diff --git a/package.json b/package.json index 4b5b43bc..e3c6f8b0 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,25 @@ "node": ">=18.0.0" }, "scripts": { + "prepare": "husky", "publish-modules": "npx lerna run build && npx lerna publish", "build": "npx lerna run build", - "lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix && prettier --write \"**/*.{js,jsx,ts,tsx,json}\" && yarn constraints --fix", + "lint:js": "eslint . --ext .js,.jsx,.ts,.tsx --fix && prettier --write \"packages/**/*.{js,jsx,ts,tsx,json}\" && yarn constraints --fix", + "lint:clang": "./scripts/run-clang-format.sh", + "lint:swift": "./scripts/run-swiftlint.sh", "test": "jest", "ios:install": "npx lerna run ios:install", "ios:test": "npx lerna run ios:test" }, "lint-staged": { - "**/*.{js,jsx,ts,tsx}": [ - "yarn lint" - ] + "*.{js,jsx,ts,tsx}": "yarn lint:js", + "*.{m,mm,h}": "yarn lint:clang", + "*.swift": "yarn lint:swift" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } }, "devDependencies": { "@babel/core": "^7.9.0", @@ -44,6 +52,7 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-native": "^4.1.0", + "husky": "^9.1.5", "jest": "^29.7.0", "lerna": "^8.1.5", "lint-staged": "^15.2.10", diff --git a/packages/core/ios/RNEmbrace/CodePushHelper.h b/packages/core/ios/RNEmbrace/CodePushHelper.h index 92db9291..f3056dd3 100644 --- a/packages/core/ios/RNEmbrace/CodePushHelper.h +++ b/packages/core/ios/RNEmbrace/CodePushHelper.h @@ -1,6 +1,6 @@ #import -@interface CodePushHelper: NSObject +@interface CodePushHelper : NSObject + (NSURL *)getCodePushURL; diff --git a/packages/core/ios/RNEmbrace/CodePushHelper.m b/packages/core/ios/RNEmbrace/CodePushHelper.m index d2cae9f6..0bd21d66 100644 --- a/packages/core/ios/RNEmbrace/CodePushHelper.m +++ b/packages/core/ios/RNEmbrace/CodePushHelper.m @@ -6,8 +6,7 @@ @implementation CodePushHelper -+ (NSURL *)getCodePushURL -{ ++ (NSURL *)getCodePushURL { #if __has_include() return [CodePush bundleURL]; #else diff --git a/packages/core/ios/RNEmbrace/EmbraceManager.m b/packages/core/ios/RNEmbrace/EmbraceManager.m old mode 100755 new mode 100644 diff --git a/packages/core/ios/RNEmbrace/EmbraceManager.swift b/packages/core/ios/RNEmbrace/EmbraceManager.swift index b74c6672..76df4a76 100644 --- a/packages/core/ios/RNEmbrace/EmbraceManager.swift +++ b/packages/core/ios/RNEmbrace/EmbraceManager.swift @@ -143,7 +143,7 @@ class EmbraceManager: NSObject { } // Should match strings defined in: packages/core/src/interfaces/Types.ts - private func lastRunEndStateToString(endState: LastRunEndState) -> String{ + private func lastRunEndStateToString(endState: LastRunEndState) -> String { switch endState { case .crash: return "CRASH" @@ -224,7 +224,7 @@ class EmbraceManager: NSObject { resolve(false) return } - + let bundleID = try computeBundleID(path: url.path) try Embrace.client?.metadata.addResource(key: REACT_NATIVE_BUNDLE_ID_RESOURCE_KEY, value: bundleID.id, lifespan: .process) resolve(true) @@ -326,11 +326,11 @@ class EmbraceManager: NSObject { reject("LOG_MESSAGE_INVALID_PROPERTIES", "Properties should be [String: String]", nil) return } - - if (!stacktrace.isEmpty) { + + if !stacktrace.isEmpty { attributes.updateValue(stacktrace, forKey: "emb.stacktrace.rn") } - + Embrace.client?.log( message, severity: severityValue, @@ -734,17 +734,17 @@ class EmbraceManager: NSObject { reject("LOG_HANDLED_ERROR_ERROR", "Error recording a log handled error, Embrace SDK may not be initialized", nil) return } - + guard var attributes = properties as? [String: String] else { reject("LOG_MESSAGE_INVALID_PROPERTIES", "Properties should be [String: String]", nil) return } - + // injecting stacktrace as attribute attributes.updateValue(stacktrace, forKey: "emb.stacktrace.rn") // not added by native sdk attributes.updateValue("handled", forKey: "emb.exception_handling") - + Embrace.client?.log( message, severity: LogSeverity.error, @@ -752,12 +752,11 @@ class EmbraceManager: NSObject { attributes: attributes, // will always include a js stacktrace as per implementation stackTraceBehavior: StackTraceBehavior.notIncluded - ); - + ) + resolve(true) } - - + @objc(logUnhandledJSException:message:type:stacktrace:resolver:rejecter:) func logUnhandledJSException( _ name: String, @@ -782,7 +781,7 @@ class EmbraceManager: NSObject { "exception.message": message, "exception.type": type, "exception.id": jsExceptionUUID - ]; + ] Embrace.client?.log( name, @@ -791,7 +790,7 @@ class EmbraceManager: NSObject { attributes: attributes, // will always include a js stacktrace as per implementation stackTraceBehavior: StackTraceBehavior.notIncluded - ); + ) do { // adding crash metadata diff --git a/packages/core/ios/RNEmbrace/RNEmbrace-Bridging-Header.h b/packages/core/ios/RNEmbrace/RNEmbrace-Bridging-Header.h index 1f362dd9..ade0a160 100644 --- a/packages/core/ios/RNEmbrace/RNEmbrace-Bridging-Header.h +++ b/packages/core/ios/RNEmbrace/RNEmbrace-Bridging-Header.h @@ -1,2 +1,3 @@ #import + #import "CodePushHelper.h" diff --git a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace.swift b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace.swift index dcab70a9..c054231f 100644 --- a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace.swift +++ b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace.swift @@ -7,7 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { EmbraceInitializer.start() let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) let rootView = RCTRootView(bridge: bridge!, moduleName: "test741", initialProperties: nil) - + rootView.backgroundColor = UIColor.white self.window = UIWindow(frame: UIScreen.main.bounds) diff --git a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace5x.swift b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace5x.swift index 486fdc1e..993452ef 100644 --- a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace5x.swift +++ b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithEmbrace5x.swift @@ -5,10 +5,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - Embrace.sharedInstance().start(launchOptions: launchOptions, framework:.reactNative) + Embrace.sharedInstance().start(launchOptions: launchOptions, framework: .reactNative) let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) let rootView = RCTRootView(bridge: bridge!, moduleName: "test741", initialProperties: nil) - + rootView.backgroundColor = UIColor.white self.window = UIWindow(frame: UIScreen.main.bounds) diff --git a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithoutEmbrace.swift b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithoutEmbrace.swift index ca68bf45..4c11720d 100644 --- a/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithoutEmbrace.swift +++ b/packages/core/scripts/__tests__/__mocks__/ios/AppDelegateWithoutEmbrace.swift @@ -6,7 +6,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) let rootView = RCTRootView(bridge: bridge!, moduleName: "test741", initialProperties: nil) - + rootView.backgroundColor = UIColor.white self.window = UIWindow(frame: UIScreen.main.bounds) diff --git a/packages/core/scripts/setup/patches/patch_ios_5x.ts b/packages/core/scripts/setup/patches/patch_ios_5x.ts index ca591853..41f3a103 100644 --- a/packages/core/scripts/setup/patches/patch_ios_5x.ts +++ b/packages/core/scripts/setup/patches/patch_ios_5x.ts @@ -19,7 +19,7 @@ export const EMBRACE_INIT_OBJECTIVEC_5X = export const EMBRACE_IMPORT_SWIFT_5X = "import Embrace"; export const EMBRACE_INIT_SWIFT_5X = - "Embrace.sharedInstance().start(launchOptions: launchOptions, framework:.reactNative)"; + "Embrace.sharedInstance().start(launchOptions: launchOptions, framework: .reactNative)"; export const PATCH_IOS_SWIFT_APPDELEGATE_5X: IPatchDefinition = { fileName: MAIN_CLASS_BY_LANGUAGE.swift, diff --git a/packages/core/test-project/ios/RNEmbraceTests/RNEmbraceTests.swift b/packages/core/test-project/ios/RNEmbraceTests/RNEmbraceTests.swift index d7c56d17..4920569f 100644 --- a/packages/core/test-project/ios/RNEmbraceTests/RNEmbraceTests.swift +++ b/packages/core/test-project/ios/RNEmbraceTests/RNEmbraceTests.swift @@ -116,7 +116,7 @@ class EmbraceManagerTests: XCTestCase { // Set a fake endpoint for unit tests otherwise we'll end up sending actual payloads to Embrace endpoints: Embrace.Endpoints(baseURL: "http://localhost/dev/null", developmentBaseURL: "http://localhost/dev/null", - configBaseURL: "http://localhost/dev/null"), + configBaseURL: "http://localhost/dev/null"), export: OpenTelemetryExport( spanExporter: self.spanExporter, diff --git a/packages/react-native-navigation/src/NavigationTracker.ts b/packages/react-native-navigation/src/NavigationTracker.ts index 31c6ec7e..e384be21 100644 --- a/packages/react-native-navigation/src/NavigationTracker.ts +++ b/packages/react-native-navigation/src/NavigationTracker.ts @@ -1,4 +1,5 @@ import {NativeModules} from "react-native"; + import { ICurrentScreenInstance, INavigation, @@ -17,8 +18,9 @@ export default class NavigationTracker { }; this.currentScreen = cS; if (NativeModules.EmbraceManager.startView) { - this.currentScreen["spanId"] = - await NativeModules.EmbraceManager.startView(cS.name); + this.currentScreen.spanId = await NativeModules.EmbraceManager.startView( + cS.name, + ); } else { console.warn( "[Embrace] The method startView was not found, please update the native SDK", @@ -28,13 +30,8 @@ export default class NavigationTracker { public updateLastScreen = async (name: string) => { if (this.currentScreen && this.currentScreen.name !== name) { - if ( - NativeModules.EmbraceManager.endView && - this.currentScreen["spanId"] - ) { - await NativeModules.EmbraceManager.endView( - this.currentScreen["spanId"], - ); + if (NativeModules.EmbraceManager.endView && this.currentScreen.spanId) { + await NativeModules.EmbraceManager.endView(this.currentScreen.spanId); this.setLastScreenStart(name); } else { console.warn( diff --git a/packages/react-navigation/src/index.ts b/packages/react-navigation/src/index.ts index 1c0ef316..3d069919 100644 --- a/packages/react-navigation/src/index.ts +++ b/packages/react-navigation/src/index.ts @@ -28,7 +28,7 @@ export const useEmbraceNavigationTracker = ( name, }; if (NativeModules.EmbraceManager.startView) { - currentScreen.current["spanId"] = + currentScreen.current.spanId = await NativeModules.EmbraceManager.startView(name); } else { console.warn( diff --git a/scripts/run-clang-format.sh b/scripts/run-clang-format.sh new file mode 100755 index 00000000..36ae3532 --- /dev/null +++ b/scripts/run-clang-format.sh @@ -0,0 +1,72 @@ +#!/bin/bash +FORMATTER="clang-format" + +# install if the formatter is not found +if ! command -v ${FORMATTER} &> /dev/null; then + echo "${FORMATTER} not found. Proceeding to install." + + # Install llvm (includes clang-format) + brew install llvm + + export PATH="/opt/homebrew/opt/llvm/bin:$PATH" +fi + +# 1) Paths to formart +SOURCE_DIRS=( + "$(pwd)/integration-tests/basic-test-app/ios/basictestapp" + "$(pwd)/examples/react-native-test-suite" + "$(pwd)/packages" + "$(pwd)/packages/core/test-project" +) + +# find command +FIND_CMD="find" +for dir in "${SOURCE_DIRS[@]}"; do + FIND_CMD+=" $dir" +done + +# 2) Exclude directories. Add more directories if needed +EXCLUDE_DIRS=( + "$(pwd)/node_modules" + "$(pwd)/build" + "$(pwd)/packages/core/scripts/__tests__" +) + +EXCLUDE_CMD="" +EXCLUDE_COUNTER=1 +EXCLUDE_DIRS_LEN=${#EXCLUDE_DIRS[@]} +for exc_dir in "${EXCLUDE_DIRS[@]}"; do + if [ $EXCLUDE_COUNTER -eq $EXCLUDE_DIRS_LEN ]; then + # do not add '-o' at the end + EXCLUDE_CMD+=" -path $exc_dir -prune" + else + EXCLUDE_CMD+=" -path $exc_dir -prune -o" + fi + ((EXCLUDE_COUNTER++)) +done + +# 3) Include file extensions. Add more file extensions if needed +FILE_EXTS=( + # "*.m" // disabling linting *.m files by now until we improve what this formatter produces + "*.mm" + "*.h" +) + +FILE_CMD="" +FILE_EXTS_COUNTER=1 +FILE_EXT_LEN=${#FILE_EXTS[@]} +for file_ext in "${FILE_EXTS[@]}"; do + if [ $FILE_EXTS_COUNTER -eq $FILE_EXT_LEN ]; then + # do not add '-o' at the end + FILE_CMD+=" -name '$file_ext'" + else + FILE_CMD+=" -name '$file_ext' -o" + fi + ((FILE_EXTS_COUNTER++)) +done + +# 4) final eval +FIND_CMD+=" -type d \( ${EXCLUDE_CMD} \) -prune -o -type f \( ${FILE_CMD} \) -print" + +# 5) Execute the find command and run `clang-format` on each file +eval "$FIND_CMD" | xargs -r clang-format -i diff --git a/scripts/run-swiftlint.sh b/scripts/run-swiftlint.sh new file mode 100644 index 00000000..c506397b --- /dev/null +++ b/scripts/run-swiftlint.sh @@ -0,0 +1,13 @@ +#!/bin/bash +FORMATTER="swiftlint" + +# install if the formatter is not found +if ! command -v ${FORMATTER} &> /dev/null; then + echo "${FORMATTER} not found. Proceeding to install." + + brew install swiftlint + + export PATH="/opt/homebrew/opt/swiftlint/bin:$PATH" +fi + +swiftlint --fix \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 27d2a0ec..b4dce937 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5869,6 +5869,7 @@ __metadata: eslint-plugin-prettier: "npm:^5.1.3" eslint-plugin-react: "npm:^7.34.1" eslint-plugin-react-native: "npm:^4.1.0" + husky: "npm:^9.1.5" jest: "npm:^29.7.0" lerna: "npm:^8.1.5" lint-staged: "npm:^15.2.10" @@ -7556,6 +7557,15 @@ __metadata: languageName: node linkType: hard +"husky@npm:^9.1.5": + version: 9.1.6 + resolution: "husky@npm:9.1.6" + bin: + husky: bin.js + checksum: 10c0/705673db4a247c1febd9c5df5f6a3519106cf0335845027bb50a15fba9b1f542cb2610932ede96fd08008f6d9f49db0f15560509861808b0031cdc0e7c798bac + languageName: node + linkType: hard + "iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24"