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