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