1*19a0af8dSWill Schurman// Copyright 2020-present 650 Industries. All rights reserved. 2*19a0af8dSWill Schurman 3*19a0af8dSWill Schurman#import "EXAppFetcher.h" 4*19a0af8dSWill Schurman#import "EXDevelopmentHomeLoader.h" 5*19a0af8dSWill Schurman#import "EXClientReleaseType.h" 6*19a0af8dSWill Schurman#import "EXEnvironment.h" 7*19a0af8dSWill Schurman#import "EXErrorRecoveryManager.h" 8*19a0af8dSWill Schurman#import "EXFileDownloader.h" 9*19a0af8dSWill Schurman#import "EXKernel.h" 10*19a0af8dSWill Schurman#import "EXKernelLinkingManager.h" 11*19a0af8dSWill Schurman#import "EXManifestResource.h" 12*19a0af8dSWill Schurman#import "EXSession.h" 13*19a0af8dSWill Schurman#import "EXUpdatesDatabaseManager.h" 14*19a0af8dSWill Schurman#import "EXVersions.h" 15*19a0af8dSWill Schurman#import "EXBuildConstants.h" 16*19a0af8dSWill Schurman 17*19a0af8dSWill Schurman#if defined(EX_DETACHED) 18*19a0af8dSWill Schurman#import "ExpoKit-Swift.h" 19*19a0af8dSWill Schurman#else 20*19a0af8dSWill Schurman#import "Expo_Go-Swift.h" 21*19a0af8dSWill Schurman#endif // defined(EX_DETACHED) 22*19a0af8dSWill Schurman 23*19a0af8dSWill Schurman#import <React/RCTUtils.h> 24*19a0af8dSWill Schurman#import <sys/utsname.h> 25*19a0af8dSWill Schurman 26*19a0af8dSWill Schurman@import EXManifests; 27*19a0af8dSWill Schurman@import EXUpdates; 28*19a0af8dSWill Schurman 29*19a0af8dSWill SchurmanNS_ASSUME_NONNULL_BEGIN 30*19a0af8dSWill Schurman 31*19a0af8dSWill Schurman@interface EXDevelopmentHomeLoader () 32*19a0af8dSWill Schurman 33*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) EXManifestAndAssetRequestHeaders *manifestAndAssetRequestHeaders; 34*19a0af8dSWill Schurman 35*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) EXManifestsManifest *confirmedManifest; 36*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) EXManifestsManifest *optimisticManifest; 37*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) NSData *bundle; 38*19a0af8dSWill Schurman@property (nonatomic, assign) BOOL isUpToDate; 39*19a0af8dSWill Schurman 40*19a0af8dSWill Schurman/** 41*19a0af8dSWill Schurman * Stateful variable to let us prevent multiple simultaneous fetches from the development server. 42*19a0af8dSWill Schurman * This can happen when reloading a bundle with remote debugging enabled; 43*19a0af8dSWill Schurman * RN requests the bundle multiple times for some reason. 44*19a0af8dSWill Schurman */ 45*19a0af8dSWill Schurman@property (nonatomic, assign) BOOL isLoadingDevelopmentJavaScriptResource; 46*19a0af8dSWill Schurman 47*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) NSError *error; 48*19a0af8dSWill Schurman 49*19a0af8dSWill Schurman@property (nonatomic, strong) dispatch_queue_t appLoaderQueue; 50*19a0af8dSWill Schurman 51*19a0af8dSWill Schurman@end 52*19a0af8dSWill Schurman 53*19a0af8dSWill Schurman@implementation EXDevelopmentHomeLoader 54*19a0af8dSWill Schurman 55*19a0af8dSWill Schurman@synthesize bundle = _bundle; 56*19a0af8dSWill Schurman@synthesize isUpToDate = _isUpToDate; 57*19a0af8dSWill Schurman 58*19a0af8dSWill Schurman- (instancetype)init { 59*19a0af8dSWill Schurman if (self = [super init]) { 60*19a0af8dSWill Schurman _manifestAndAssetRequestHeaders = [EXDevelopmentHomeLoader bundledDevelopmentHomeManifestAndAssetRequestHeaders]; 61*19a0af8dSWill Schurman _appLoaderQueue = dispatch_queue_create("host.exp.exponent.LoaderQueue", DISPATCH_QUEUE_SERIAL); 62*19a0af8dSWill Schurman } 63*19a0af8dSWill Schurman return self; 64*19a0af8dSWill Schurman} 65*19a0af8dSWill Schurman 66*19a0af8dSWill Schurman#pragma mark - getters and lifecycle 67*19a0af8dSWill Schurman 68*19a0af8dSWill Schurman- (void)_reset 69*19a0af8dSWill Schurman{ 70*19a0af8dSWill Schurman _confirmedManifest = nil; 71*19a0af8dSWill Schurman _optimisticManifest = nil; 72*19a0af8dSWill Schurman _bundle = nil; 73*19a0af8dSWill Schurman _error = nil; 74*19a0af8dSWill Schurman _isUpToDate = NO; 75*19a0af8dSWill Schurman _isLoadingDevelopmentJavaScriptResource = NO; 76*19a0af8dSWill Schurman} 77*19a0af8dSWill Schurman 78*19a0af8dSWill Schurman- (EXAppLoaderStatus)status 79*19a0af8dSWill Schurman{ 80*19a0af8dSWill Schurman if (_error) { 81*19a0af8dSWill Schurman return kEXAppLoaderStatusError; 82*19a0af8dSWill Schurman } else if (_bundle) { 83*19a0af8dSWill Schurman return kEXAppLoaderStatusHasManifestAndBundle; 84*19a0af8dSWill Schurman } else if (_optimisticManifest) { 85*19a0af8dSWill Schurman return kEXAppLoaderStatusHasManifest; 86*19a0af8dSWill Schurman } 87*19a0af8dSWill Schurman return kEXAppLoaderStatusNew; 88*19a0af8dSWill Schurman} 89*19a0af8dSWill Schurman 90*19a0af8dSWill Schurman- (nullable EXManifestsManifest *)manifest 91*19a0af8dSWill Schurman{ 92*19a0af8dSWill Schurman if (_confirmedManifest) { 93*19a0af8dSWill Schurman return _confirmedManifest; 94*19a0af8dSWill Schurman } 95*19a0af8dSWill Schurman if (_optimisticManifest) { 96*19a0af8dSWill Schurman return _optimisticManifest; 97*19a0af8dSWill Schurman } 98*19a0af8dSWill Schurman return nil; 99*19a0af8dSWill Schurman} 100*19a0af8dSWill Schurman 101*19a0af8dSWill Schurman- (nullable NSData *)bundle 102*19a0af8dSWill Schurman{ 103*19a0af8dSWill Schurman if (_bundle) { 104*19a0af8dSWill Schurman return _bundle; 105*19a0af8dSWill Schurman } 106*19a0af8dSWill Schurman return nil; 107*19a0af8dSWill Schurman} 108*19a0af8dSWill Schurman 109*19a0af8dSWill Schurman- (void)forceBundleReload 110*19a0af8dSWill Schurman{ 111*19a0af8dSWill Schurman if (self.status == kEXAppLoaderStatusNew) { 112*19a0af8dSWill Schurman @throw [NSException exceptionWithName:NSInternalInconsistencyException 113*19a0af8dSWill Schurman reason:@"Tried to load a bundle from an AppLoader with no manifest." 114*19a0af8dSWill Schurman userInfo:@{}]; 115*19a0af8dSWill Schurman } 116*19a0af8dSWill Schurman NSAssert([self supportsBundleReload], @"Tried to force a bundle reload on a non-development bundle"); 117*19a0af8dSWill Schurman if (self.isLoadingDevelopmentJavaScriptResource) { 118*19a0af8dSWill Schurman // prevent multiple simultaneous fetches from the development server. 119*19a0af8dSWill Schurman // this can happen when reloading a bundle with remote debugging enabled; 120*19a0af8dSWill Schurman // RN requests the bundle multiple times for some reason. 121*19a0af8dSWill Schurman // TODO: fix inside of RN 122*19a0af8dSWill Schurman return; 123*19a0af8dSWill Schurman } 124*19a0af8dSWill Schurman [self _loadDevelopmentJavaScriptResource]; 125*19a0af8dSWill Schurman} 126*19a0af8dSWill Schurman 127*19a0af8dSWill Schurman- (BOOL)supportsBundleReload 128*19a0af8dSWill Schurman{ 129*19a0af8dSWill Schurman if (_optimisticManifest) { 130*19a0af8dSWill Schurman return _optimisticManifest.isUsingDeveloperTool; 131*19a0af8dSWill Schurman } 132*19a0af8dSWill Schurman return NO; 133*19a0af8dSWill Schurman} 134*19a0af8dSWill Schurman 135*19a0af8dSWill Schurman#pragma mark - public 136*19a0af8dSWill Schurman 137*19a0af8dSWill Schurman- (void)request 138*19a0af8dSWill Schurman{ 139*19a0af8dSWill Schurman [self _reset]; 140*19a0af8dSWill Schurman [self _beginRequest]; 141*19a0af8dSWill Schurman} 142*19a0af8dSWill Schurman 143*19a0af8dSWill Schurman- (void)requestFromCache 144*19a0af8dSWill Schurman{ 145*19a0af8dSWill Schurman [self request]; 146*19a0af8dSWill Schurman} 147*19a0af8dSWill Schurman 148*19a0af8dSWill Schurman#pragma mark - EXHomeAppLoaderTaskDelegate 149*19a0af8dSWill Schurman 150*19a0af8dSWill Schurman- (void)homeAppLoaderTask:(EXHomeAppLoaderTask *)appLoaderTask didFinishWithLauncher:(id<EXUpdatesAppLauncher>)launcher 151*19a0af8dSWill Schurman{ 152*19a0af8dSWill Schurman if (_error) { 153*19a0af8dSWill Schurman return; 154*19a0af8dSWill Schurman } 155*19a0af8dSWill Schurman 156*19a0af8dSWill Schurman if (!_optimisticManifest) { 157*19a0af8dSWill Schurman [self _setOptimisticManifest:launcher.launchedUpdate.manifest]; 158*19a0af8dSWill Schurman } 159*19a0af8dSWill Schurman 160*19a0af8dSWill Schurman // HomeAppLoaderTask always sets this to true 161*19a0af8dSWill Schurman _isUpToDate = true; 162*19a0af8dSWill Schurman 163*19a0af8dSWill Schurman if (launcher.launchedUpdate.manifest.isUsingDeveloperTool) { 164*19a0af8dSWill Schurman // in dev mode, we need to set an optimistic manifest but nothing else 165*19a0af8dSWill Schurman return; 166*19a0af8dSWill Schurman } 167*19a0af8dSWill Schurman _confirmedManifest = launcher.launchedUpdate.manifest; 168*19a0af8dSWill Schurman if (_confirmedManifest == nil) { 169*19a0af8dSWill Schurman return; 170*19a0af8dSWill Schurman } 171*19a0af8dSWill Schurman _bundle = [NSData dataWithContentsOfURL:launcher.launchAssetUrl]; 172*19a0af8dSWill Schurman 173*19a0af8dSWill Schurman if (self.delegate) { 174*19a0af8dSWill Schurman [self.delegate appLoader:self didFinishLoadingManifest:_confirmedManifest bundle:_bundle]; 175*19a0af8dSWill Schurman } 176*19a0af8dSWill Schurman} 177*19a0af8dSWill Schurman 178*19a0af8dSWill Schurman- (void)homeAppLoaderTask:(EXHomeAppLoaderTask *)appLoaderTask didFinishWithError:(NSError *)error 179*19a0af8dSWill Schurman{ 180*19a0af8dSWill Schurman _error = error; 181*19a0af8dSWill Schurman 182*19a0af8dSWill Schurman if (self.delegate) { 183*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:_error]; 184*19a0af8dSWill Schurman } 185*19a0af8dSWill Schurman} 186*19a0af8dSWill Schurman 187*19a0af8dSWill Schurman#pragma mark - internal 188*19a0af8dSWill Schurman 189*19a0af8dSWill Schurman- (BOOL)_initializeDatabase 190*19a0af8dSWill Schurman{ 191*19a0af8dSWill Schurman EXUpdatesDatabaseManager *updatesDatabaseManager = [EXKernel sharedInstance].serviceRegistry.updatesDatabaseManager; 192*19a0af8dSWill Schurman BOOL success = updatesDatabaseManager.isDatabaseOpen; 193*19a0af8dSWill Schurman if (!updatesDatabaseManager.isDatabaseOpen) { 194*19a0af8dSWill Schurman success = [updatesDatabaseManager openDatabase]; 195*19a0af8dSWill Schurman } 196*19a0af8dSWill Schurman 197*19a0af8dSWill Schurman if (!success) { 198*19a0af8dSWill Schurman _error = updatesDatabaseManager.error; 199*19a0af8dSWill Schurman if (self.delegate) { 200*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:_error]; 201*19a0af8dSWill Schurman } 202*19a0af8dSWill Schurman return NO; 203*19a0af8dSWill Schurman } else { 204*19a0af8dSWill Schurman return YES; 205*19a0af8dSWill Schurman } 206*19a0af8dSWill Schurman} 207*19a0af8dSWill Schurman 208*19a0af8dSWill Schurman- (void)_beginRequest 209*19a0af8dSWill Schurman{ 210*19a0af8dSWill Schurman if (![self _initializeDatabase]) { 211*19a0af8dSWill Schurman return; 212*19a0af8dSWill Schurman } 213*19a0af8dSWill Schurman [self _startLoaderTask]; 214*19a0af8dSWill Schurman} 215*19a0af8dSWill Schurman 216*19a0af8dSWill Schurman- (void)_startLoaderTask 217*19a0af8dSWill Schurman{ 218*19a0af8dSWill Schurman EXUpdatesConfig *config = [EXUpdatesConfig configFromDictionary:@{ 219*19a0af8dSWill Schurman EXUpdatesConfig.EXUpdatesConfigHasEmbeddedUpdateKey: @NO, 220*19a0af8dSWill Schurman EXUpdatesConfig.EXUpdatesConfigSDKVersionKey: [self _sdkVersions], 221*19a0af8dSWill Schurman EXUpdatesConfig.EXUpdatesConfigScopeKeyKey: self.manifestAndAssetRequestHeaders.manifest.scopeKey, 222*19a0af8dSWill Schurman EXUpdatesConfig.EXUpdatesConfigExpectsSignedManifestKey: @YES, 223*19a0af8dSWill Schurman EXUpdatesConfig.EXUpdatesConfigRequestHeadersKey: [self _requestHeaders] 224*19a0af8dSWill Schurman }]; 225*19a0af8dSWill Schurman 226*19a0af8dSWill Schurman EXUpdatesDatabaseManager *updatesDatabaseManager = [EXKernel sharedInstance].serviceRegistry.updatesDatabaseManager; 227*19a0af8dSWill Schurman 228*19a0af8dSWill Schurman NSMutableArray *sdkVersions = [[EXVersions sharedInstance].versions[@"sdkVersions"] ?: @[[EXVersions sharedInstance].temporarySdkVersion] mutableCopy]; 229*19a0af8dSWill Schurman [sdkVersions addObject:@"UNVERSIONED"]; 230*19a0af8dSWill Schurman 231*19a0af8dSWill Schurman NSMutableArray *sdkVersionRuntimeVersions = [[NSMutableArray alloc] initWithCapacity:sdkVersions.count]; 232*19a0af8dSWill Schurman for (NSString *sdkVersion in sdkVersions) { 233*19a0af8dSWill Schurman [sdkVersionRuntimeVersions addObject:[NSString stringWithFormat:@"exposdk:%@", sdkVersion]]; 234*19a0af8dSWill Schurman } 235*19a0af8dSWill Schurman [sdkVersionRuntimeVersions addObject:@"exposdk:UNVERSIONED"]; 236*19a0af8dSWill Schurman [sdkVersions addObjectsFromArray:sdkVersionRuntimeVersions]; 237*19a0af8dSWill Schurman 238*19a0af8dSWill Schurman EXUpdatesSelectionPolicy *selectionPolicy = [[EXUpdatesSelectionPolicy alloc] 239*19a0af8dSWill Schurman initWithLauncherSelectionPolicy:[[EXExpoGoLauncherSelectionPolicyFilterAware alloc] initWithSdkVersions:sdkVersions] 240*19a0af8dSWill Schurman loaderSelectionPolicy:[EXUpdatesLoaderSelectionPolicyFilterAware new] 241*19a0af8dSWill Schurman reaperSelectionPolicy:[EXUpdatesReaperSelectionPolicyDevelopmentClient new]]; 242*19a0af8dSWill Schurman 243*19a0af8dSWill Schurman EXHomeAppLoaderTask *loaderTask = [[EXHomeAppLoaderTask alloc] initWithManifestAndAssetRequestHeaders:self.manifestAndAssetRequestHeaders 244*19a0af8dSWill Schurman config:config 245*19a0af8dSWill Schurman database:updatesDatabaseManager.database 246*19a0af8dSWill Schurman directory:updatesDatabaseManager.updatesDirectory 247*19a0af8dSWill Schurman selectionPolicy:selectionPolicy 248*19a0af8dSWill Schurman delegateQueue:_appLoaderQueue]; 249*19a0af8dSWill Schurman loaderTask.delegate = self; 250*19a0af8dSWill Schurman [loaderTask start]; 251*19a0af8dSWill Schurman} 252*19a0af8dSWill Schurman 253*19a0af8dSWill Schurman- (void)_setOptimisticManifest:(EXManifestsManifest *)manifest 254*19a0af8dSWill Schurman{ 255*19a0af8dSWill Schurman _optimisticManifest = manifest; 256*19a0af8dSWill Schurman if (self.delegate) { 257*19a0af8dSWill Schurman [self.delegate appLoader:self didLoadOptimisticManifest:_optimisticManifest]; 258*19a0af8dSWill Schurman } 259*19a0af8dSWill Schurman} 260*19a0af8dSWill Schurman 261*19a0af8dSWill Schurman- (void)_loadDevelopmentJavaScriptResource 262*19a0af8dSWill Schurman{ 263*19a0af8dSWill Schurman _isLoadingDevelopmentJavaScriptResource = YES; 264*19a0af8dSWill Schurman EXAppFetcher *appFetcher = [[EXAppFetcher alloc] initWithAppLoader:self]; 265*19a0af8dSWill Schurman [appFetcher fetchJSBundleWithManifest:self.optimisticManifest cacheBehavior:EXCachedResourceNoCache timeoutInterval:kEXJSBundleTimeout progress:^(EXLoadingProgress *progress) { 266*19a0af8dSWill Schurman if (self.delegate) { 267*19a0af8dSWill Schurman [self.delegate appLoader:self didLoadBundleWithProgress:progress]; 268*19a0af8dSWill Schurman } 269*19a0af8dSWill Schurman } success:^(NSData *bundle) { 270*19a0af8dSWill Schurman self.isUpToDate = YES; 271*19a0af8dSWill Schurman self.bundle = bundle; 272*19a0af8dSWill Schurman self.isLoadingDevelopmentJavaScriptResource = NO; 273*19a0af8dSWill Schurman if (self.delegate) { 274*19a0af8dSWill Schurman [self.delegate appLoader:self didFinishLoadingManifest:self.optimisticManifest bundle:self.bundle]; 275*19a0af8dSWill Schurman } 276*19a0af8dSWill Schurman } error:^(NSError *error) { 277*19a0af8dSWill Schurman self.error = error; 278*19a0af8dSWill Schurman self.isLoadingDevelopmentJavaScriptResource = NO; 279*19a0af8dSWill Schurman if (self.delegate) { 280*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:error]; 281*19a0af8dSWill Schurman } 282*19a0af8dSWill Schurman }]; 283*19a0af8dSWill Schurman} 284*19a0af8dSWill Schurman 285*19a0af8dSWill Schurman#pragma mark - headers 286*19a0af8dSWill Schurman 287*19a0af8dSWill Schurman- (NSDictionary *)_requestHeaders 288*19a0af8dSWill Schurman{ 289*19a0af8dSWill Schurman NSDictionary *requestHeaders = @{ 290*19a0af8dSWill Schurman @"Exponent-SDK-Version": [self _sdkVersions], 291*19a0af8dSWill Schurman @"Exponent-Accept-Signature": @"true", 292*19a0af8dSWill Schurman @"Exponent-Platform": @"ios", 293*19a0af8dSWill Schurman @"Exponent-Version": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"], 294*19a0af8dSWill Schurman @"Expo-Client-Environment": [self _clientEnvironment], 295*19a0af8dSWill Schurman @"Expo-Updates-Environment": [self _clientEnvironment], 296*19a0af8dSWill Schurman @"User-Agent": [self _userAgentString], 297*19a0af8dSWill Schurman @"Expo-Client-Release-Type": [EXClientReleaseType clientReleaseType] 298*19a0af8dSWill Schurman }; 299*19a0af8dSWill Schurman 300*19a0af8dSWill Schurman NSString *sessionSecret = [[EXSession sharedInstance] sessionSecret]; 301*19a0af8dSWill Schurman if (sessionSecret) { 302*19a0af8dSWill Schurman NSMutableDictionary *requestHeadersMutable = [requestHeaders mutableCopy]; 303*19a0af8dSWill Schurman requestHeadersMutable[@"Expo-Session"] = sessionSecret; 304*19a0af8dSWill Schurman requestHeaders = requestHeadersMutable; 305*19a0af8dSWill Schurman } 306*19a0af8dSWill Schurman 307*19a0af8dSWill Schurman return requestHeaders; 308*19a0af8dSWill Schurman} 309*19a0af8dSWill Schurman 310*19a0af8dSWill Schurman- (NSString *)_userAgentString 311*19a0af8dSWill Schurman{ 312*19a0af8dSWill Schurman struct utsname systemInfo; 313*19a0af8dSWill Schurman uname(&systemInfo); 314*19a0af8dSWill Schurman NSString *deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; 315*19a0af8dSWill Schurman return [NSString stringWithFormat:@"Exponent/%@ (%@; %@ %@; Scale/%.2f; %@)", 316*19a0af8dSWill Schurman [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"], 317*19a0af8dSWill Schurman deviceModel, 318*19a0af8dSWill Schurman [UIDevice currentDevice].systemName, 319*19a0af8dSWill Schurman [UIDevice currentDevice].systemVersion, 320*19a0af8dSWill Schurman [UIScreen mainScreen].scale, 321*19a0af8dSWill Schurman [NSLocale autoupdatingCurrentLocale].localeIdentifier]; 322*19a0af8dSWill Schurman} 323*19a0af8dSWill Schurman 324*19a0af8dSWill Schurman- (NSString *)_clientEnvironment 325*19a0af8dSWill Schurman{ 326*19a0af8dSWill Schurman if ([EXEnvironment sharedEnvironment].isDetached) { 327*19a0af8dSWill Schurman return @"STANDALONE"; 328*19a0af8dSWill Schurman } else { 329*19a0af8dSWill Schurman return @"EXPO_DEVICE"; 330*19a0af8dSWill Schurman#if TARGET_IPHONE_SIMULATOR 331*19a0af8dSWill Schurman return @"EXPO_SIMULATOR"; 332*19a0af8dSWill Schurman#endif 333*19a0af8dSWill Schurman } 334*19a0af8dSWill Schurman} 335*19a0af8dSWill Schurman 336*19a0af8dSWill Schurman- (NSString *)_sdkVersions 337*19a0af8dSWill Schurman{ 338*19a0af8dSWill Schurman NSArray *versionsAvailable = [EXVersions sharedInstance].versions[@"sdkVersions"]; 339*19a0af8dSWill Schurman if (versionsAvailable) { 340*19a0af8dSWill Schurman return [versionsAvailable componentsJoinedByString:@","]; 341*19a0af8dSWill Schurman } else { 342*19a0af8dSWill Schurman return [EXVersions sharedInstance].temporarySdkVersion; 343*19a0af8dSWill Schurman } 344*19a0af8dSWill Schurman} 345*19a0af8dSWill Schurman 346*19a0af8dSWill Schurman+ (EXManifestAndAssetRequestHeaders * _Nullable)bundledDevelopmentHomeManifestAndAssetRequestHeaders 347*19a0af8dSWill Schurman{ 348*19a0af8dSWill Schurman NSString *manifestAndAssetRequestHeadersJson = [EXBuildConstants sharedInstance].kernelManifestAndAssetRequestHeadersJsonString; 349*19a0af8dSWill Schurman if (!manifestAndAssetRequestHeadersJson) { 350*19a0af8dSWill Schurman return nil; 351*19a0af8dSWill Schurman } 352*19a0af8dSWill Schurman 353*19a0af8dSWill Schurman id manifestAndAssetRequestHeaders = RCTJSONParse(manifestAndAssetRequestHeadersJson, nil); 354*19a0af8dSWill Schurman if ([manifestAndAssetRequestHeaders isKindOfClass:[NSDictionary class]]) { 355*19a0af8dSWill Schurman id manifest = manifestAndAssetRequestHeaders[@"manifest"]; 356*19a0af8dSWill Schurman id assetRequestHeaders = manifestAndAssetRequestHeaders[@"assetRequestHeaders"]; 357*19a0af8dSWill Schurman if ([manifest isKindOfClass:[NSDictionary class]]) { 358*19a0af8dSWill Schurman return [[EXManifestAndAssetRequestHeaders alloc] initWithManifest:[EXManifestsManifestFactory manifestForManifestJSON:manifest] 359*19a0af8dSWill Schurman assetRequestHeaders:assetRequestHeaders]; 360*19a0af8dSWill Schurman } 361*19a0af8dSWill Schurman } 362*19a0af8dSWill Schurman 363*19a0af8dSWill Schurman return nil; 364*19a0af8dSWill Schurman} 365*19a0af8dSWill Schurman 366*19a0af8dSWill Schurman@end 367*19a0af8dSWill Schurman 368*19a0af8dSWill SchurmanNS_ASSUME_NONNULL_END 369