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