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