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