#import "AppDelegate.h" #import #import #import #import #import #import #if RCT_NEW_ARCH_ENABLED #import #import #import #import #import #import #import static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @interface AppDelegate () { RCTTurboModuleManager *_turboModuleManager; RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @end #endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { RCTAppSetupPrepareApp(application); NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [self configurePinning]; [self disableUrlCache]; [self clearWebEditorCache]; NSString *CFBundleIdentifier = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; NSDictionary * initialProperties = @{@"env" : [CFBundleIdentifier isEqualToString:@"com.standardnotes.standardnotes.dev"] ? @"dev" : @"prod"}; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; #if RCT_NEW_ARCH_ENABLED _contextContainer = std::make_shared(); _reactNativeConfig = std::make_shared(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; #endif RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"StandardNotes" initialProperties:initialProperties]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; if (@available(iOS 13.0, *)) { [rootView setBackgroundColor:[UIColor systemBackgroundColor]]; } else { [rootView setBackgroundColor:[UIColor blackColor]]; } UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES; } /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. /// /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. - (BOOL)concurrentRootEnabled { // Switch this bool to turn on and off the concurrent root return true; } - (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; #ifdef RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } #if RCT_NEW_ARCH_ENABLED #pragma mark - RCTCxxBridgeDelegate - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge { _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); } #pragma mark RCTTurboModuleManagerDelegate - (Class)getModuleClassFromName:(const char *)name { return RCTCoreModulesClassProvider(name); } - (std::shared_ptr)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr)jsInvoker { return nullptr; } - (std::shared_ptr)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } - (id)getModuleInstanceFromClass:(Class)moduleClass { return RCTAppSetupDefaultModuleFromClass(moduleClass); } #endif - (void)disableUrlCache { // Disable NSURLCache for general network requests. Caches are not protected by NSFileProtectionComplete. // Disabling, or implementing a custom subclass are only two solutions. https://stackoverflow.com/questions/27933387/nsurlcache-and-data-protection NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache]; } - (void)clearWebEditorCache { // Clear web editor cache after every app update NSString *lastVersionClearKey = @"lastVersionClearKey"; NSString *lastVersionClear = [[NSUserDefaults standardUserDefaults] objectForKey:lastVersionClearKey]; NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; if(![currentVersion isEqualToString:lastVersionClear]) { // UIWebView [[NSURLCache sharedURLCache] removeAllCachedResponses]; // WebKit NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{}]; [[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:lastVersionClearKey]; } } - (void)configurePinning { if(@available(iOS 10, *)) { NSDictionary *trustKitConfig = @{ kTSKSwizzleNetworkDelegates: @YES, // The list of domains we want to pin and their configuration kTSKPinnedDomains: @{ @"standardnotes.org" : @{ kTSKIncludeSubdomains:@YES, kTSKEnforcePinning:@YES, // Send reports for pin validation failures so we can track them kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"], // The pinned public keys' Subject Public Key Info hashes kTSKPublicKeyHashes : @[ @"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=", @"C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=", @"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=", @"sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=", @"++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=", @"f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=", @"NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=", @"9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=" ], }, @"standardnotes.com" : @{ kTSKIncludeSubdomains:@YES, kTSKEnforcePinning:@YES, // Send reports for pin validation failures so we can track them kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"], // The pinned public keys' Subject Public Key Info hashes kTSKPublicKeyHashes : @[ @"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=", @"C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=", @"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=", @"sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=", @"++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=", @"f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=", @"NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=", @"9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=" ], }, } }; [TrustKit initSharedInstanceWithConfiguration:trustKitConfig]; } } @end