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