1 2#import "EXBuildConstants.h" 3#import "EXHomeAppManager.h" 4#import "EXKernel.h" 5#import "EXAppFetcher.h" 6#import "EXAppLoader.h" 7#import "EXKernelLinkingManager.h" 8#import "EXHomeModule.h" 9#import "EXKernelUtil.h" 10#import "EXLog.h" 11#import "ExpoKit.h" 12#import "EXReactAppExceptionHandler.h" 13#import "EXReactAppManager+Private.h" 14#import "EXVersionManager.h" 15#import "EXVersions.h" 16 17#import <React/RCTUtils.h> 18#import <React/RCTBridge.h> 19 20#import <EXCore/EXModuleRegistryProvider.h> 21 22NSString * const kEXHomeLaunchUrlDefaultsKey = @"EXKernelLaunchUrlDefaultsKey"; 23NSString *kEXHomeBundleResourceName = @"kernel.ios"; 24NSString *kEXHomeManifestResourceName = @"kernel-manifest"; 25 26@implementation EXHomeAppManager 27 28#pragma mark - interfacing with home JS 29 30- (void)addHistoryItemWithUrl:(NSURL *)manifestUrl manifest:(NSDictionary *)manifest 31{ 32 if (!manifest || !manifestUrl || [manifest[@"id"] isEqualToString:@"@exponent/home"]) { 33 return; 34 } 35 NSDictionary *params = @{ 36 @"manifestUrl": manifestUrl.absoluteString, 37 @"manifest": manifest, 38 }; 39 [self _dispatchHomeJSEvent:@"addHistoryItem" body:params onSuccess:nil onFailure:nil]; 40} 41 42- (void)getHistoryUrlForExperienceId:(NSString *)experienceId completion:(void (^)(NSString *))completion 43{ 44 [self _dispatchHomeJSEvent:@"getHistoryUrlForExperienceId" 45 body:@{ @"experienceId": experienceId } 46 onSuccess:^(NSDictionary *result) { 47 NSString *url = result[@"url"]; 48 completion(url); 49 } onFailure:^(NSString *errorMessage) { 50 completion(nil); 51 }]; 52} 53 54- (void)getIsValidHomeManifestToOpen:(NSDictionary *)manifest manifestUrl:(NSURL *) manifestUrl completion:(void (^)(BOOL))completion 55{ 56 [self _dispatchHomeJSEvent:@"getIsValidHomeManifestToOpen" 57 body:@{ @"manifest": manifest, @"manifestUrl": manifestUrl.absoluteString } 58 onSuccess:^(NSDictionary *result) { 59 BOOL isValid = [result[@"isValid"] boolValue]; 60 completion(isValid); 61 } onFailure:^(NSString *errorMessage) { 62 completion(NO); 63 }]; 64} 65 66- (void)showQRReader 67{ 68 [self _dispatchHomeJSEvent:@"showQRReader" body:@{} onSuccess:nil onFailure:nil]; 69} 70 71#pragma mark - EXReactAppManager 72 73- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge 74{ 75 NSMutableArray *modules = [NSMutableArray array]; 76 self.exceptionHandler = [[EXReactAppExceptionHandler alloc] initWithAppRecord:self.appRecord]; 77 78 NSMutableDictionary *params = [@{ 79 @"bridge": bridge, 80 @"browserModuleClass": [EXHomeModule class], 81 @"constants": @{ 82 @"installationId": [EXKernel deviceInstallUUID], 83 @"expoRuntimeVersion": [EXBuildConstants sharedInstance].expoRuntimeVersion, 84 @"linkingUri": @"exp://", 85 @"manifest": self.appRecord.appLoader.manifest, 86 @"appOwnership": @"expo", 87 }, 88 @"exceptionsManagerDelegate": self.exceptionHandler, 89 @"supportedSdkVersions": [EXVersions sharedInstance].versions[@"sdkVersions"], 90 @"isDeveloper": @([EXBuildConstants sharedInstance].isDevKernel), 91 @"isStandardDevMenuAllowed": @(YES), // kernel enables traditional RN dev menu 92 @"manifest": self.appRecord.appLoader.manifest, 93 @"services": [EXKernel sharedInstance].serviceRegistry.allServices, 94 @"singletonModules": [EXModuleRegistryProvider singletonModules], 95 } mutableCopy]; 96 97 98 NSURL *initialHomeUrl = [self _initialHomeUrl]; 99 if (initialHomeUrl) { 100 params[@"initialUri"] = initialHomeUrl; 101 } 102 103 [modules addObjectsFromArray:[self.versionManager extraModulesWithParams:params]]; 104 105 return modules; 106} 107 108- (void)computeVersionSymbolPrefix 109{ 110 self.validatedVersion = nil; 111 self.versionSymbolPrefix = [[EXVersions sharedInstance] symbolPrefixForSdkVersion:self.validatedVersion isKernel:YES]; 112} 113 114- (RCTLogFunction)logFunction 115{ 116 return EXGetKernelRCTLogFunction(); 117} 118 119- (RCTLogLevel)logLevel 120{ 121 return RCTLogLevelWarning; 122} 123 124- (NSDictionary *)launchOptionsForBridge 125{ 126 if (!self.hasBridgeEverLoaded) { 127 return [ExpoKit sharedInstance].launchOptions; 128 } else { 129 // don't want to re-consume launch options when the bridge reloads. 130 return nil; 131 } 132} 133 134- (NSString *)bundleResourceNameForAppFetcher:(__unused EXAppFetcher *)appFetcher withManifest:(nonnull __unused NSDictionary *)manifest 135{ 136 return kEXHomeBundleResourceName; 137} 138 139- (BOOL)appFetcherShouldInvalidateBundleCache:(__unused EXAppFetcher *)appFetcher 140{ 141 // if crashlytics shows that we're recovering from a native crash, invalidate any downloaded home cache. 142 BOOL shouldClearKernelCache = [[NSUserDefaults standardUserDefaults] boolForKey:kEXKernelClearJSCacheUserDefaultsKey]; 143 if (shouldClearKernelCache) { 144 [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXKernelClearJSCacheUserDefaultsKey]; 145 return YES; 146 } 147 return NO; 148} 149 150#pragma mark - util 151 152- (void)_dispatchHomeJSEvent:(NSString *)eventName body:(NSDictionary *)eventBody onSuccess:(void (^_Nullable)(NSDictionary * _Nullable))success onFailure:(void (^_Nullable)(NSString * _Nullable))failure 153{ 154 EXHomeModule *homeModule = [[EXKernel sharedInstance] nativeModuleForAppManager:self named:@"ExponentKernel"]; 155 if (homeModule) { 156 [homeModule dispatchJSEvent:eventName body:eventBody onSuccess:success onFailure:failure]; 157 } else { 158 if (failure) { 159 failure(nil); 160 } 161 } 162} 163 164- (NSURL *)_initialHomeUrl 165{ 166 // used by appetize - override the kernel initial url if there's something in NSUserDefaults 167 NSURL *initialHomeUrl; 168 NSString *kernelInitialUrlDefaultsValue = [[NSUserDefaults standardUserDefaults] stringForKey:kEXHomeLaunchUrlDefaultsKey]; 169 if (kernelInitialUrlDefaultsValue) { 170 initialHomeUrl = [NSURL URLWithString:kernelInitialUrlDefaultsValue]; 171 [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXHomeLaunchUrlDefaultsKey]; 172 [[NSUserDefaults standardUserDefaults] synchronize]; 173 } else { 174 initialHomeUrl = [EXKernelLinkingManager initialUrlFromLaunchOptions:[self launchOptionsForBridge]]; 175 } 176 return initialHomeUrl; 177} 178 179+ (NSDictionary * _Nullable)bundledHomeManifest 180{ 181 NSString *manifestJson = nil; 182 BOOL usesNSBundleManifest = NO; 183 184 // if developing, use development manifest from EXBuildConstants 185 if ([EXBuildConstants sharedInstance].isDevKernel) { 186 manifestJson = [EXBuildConstants sharedInstance].kernelManifestJsonString; 187 } 188 189 // otherwise use published manifest 190 if (!manifestJson) { 191 NSString *manifestPath = [[NSBundle mainBundle] pathForResource:kEXHomeManifestResourceName ofType:@"json"]; 192 if (manifestPath) { 193 NSError *error; 194 usesNSBundleManifest = YES; 195 manifestJson = [NSString stringWithContentsOfFile:manifestPath encoding:NSUTF8StringEncoding error:&error]; 196 if (error) { 197 manifestJson = nil; 198 } 199 } 200 } 201 202 if (manifestJson) { 203 id manifest = RCTJSONParse(manifestJson, nil); 204 if ([manifest isKindOfClass:[NSDictionary class]]) { 205 if (usesNSBundleManifest && ![manifest[@"id"] isEqualToString:@"@exponent/home"]) { 206 DDLogError(@"Bundled kernel manifest was published with an id other than @exponent/home"); 207 } 208 return manifest; 209 } 210 } 211 return nil; 212} 213 214- (BOOL)requiresValidManifests 215{ 216 // running home 217 return NO; 218} 219 220@end 221