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