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