1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import "EXApiUtil.h"
4#import "EXAppFetcher+Private.h"
5#import "EXAppLoader.h"
6#import "EXEnvironment.h"
7#import "EXErrorRecoveryManager.h"
8#import "EXJavaScriptResource.h"
9#import "EXKernel.h"
10#import "EXVersions.h"
11
12#import <React/RCTUtils.h>
13
14NS_ASSUME_NONNULL_BEGIN
15
16@implementation EXAppFetcher
17
18- (instancetype)initWithAppLoader:(EXAppLoader *)appLoader
19{
20  if (self = [super init]) {
21    _appLoader = appLoader;
22  }
23  return self;
24}
25
26- (void)start
27{
28  // overridden by subclasses
29  @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Should not call EXAppFetcher#start -- use a subclass instead" userInfo:nil];
30}
31
32- (void)fetchJSBundleWithManifest:(EXUpdatesRawManifest *)manifest
33                    cacheBehavior:(EXCachedResourceBehavior)cacheBehavior
34                  timeoutInterval:(NSTimeInterval)timeoutInterval
35                         progress:(void (^ _Nullable )(EXLoadingProgress *))progressBlock
36                          success:(void (^)(NSData *))successBlock
37                            error:(void (^)(NSError *))errorBlock
38{
39  EXJavaScriptResource *jsResource = [[EXJavaScriptResource alloc] initWithBundleName:[self.dataSource bundleResourceNameForAppFetcher:self withManifest:manifest]
40                                                                            remoteUrl:[EXApiUtil bundleUrlFromManifest:manifest]
41                                                                      devToolsEnabled:manifest.isUsingDeveloperTool];
42  jsResource.abiVersion = [[EXVersions sharedInstance] availableSdkVersionForManifest:manifest];
43  jsResource.requestTimeoutInterval = timeoutInterval;
44
45  EXCachedResourceBehavior behavior = cacheBehavior;
46  // if we've disabled updates, ignore all other settings and only use the cache
47  if ([EXEnvironment sharedEnvironment].isDetached && ![EXEnvironment sharedEnvironment].areRemoteUpdatesEnabled) {
48    behavior = EXCachedResourceOnlyCache;
49  }
50
51  if ([self.dataSource appFetcherShouldInvalidateBundleCache:self]) {
52    [jsResource removeCache];
53  }
54
55  [jsResource loadResourceWithBehavior:cacheBehavior progressBlock:progressBlock successBlock:successBlock errorBlock:errorBlock];
56}
57
58+ (nullable NSString *)experienceIdWithManifest:(EXUpdatesRawManifest * _Nonnull)manifest
59{
60  return manifest.rawID;
61}
62
63+ (EXCachedResourceBehavior)cacheBehaviorForJSWithManifest:(EXUpdatesRawManifest * _Nonnull)manifest
64{
65  if ([[EXKernel sharedInstance].serviceRegistry.errorRecoveryManager experienceIdIsRecoveringFromError:[[self class] experienceIdWithManifest:manifest]]) {
66    // if this experience id encountered a loading error before, discard any cache we might have
67    return EXCachedResourceWriteToCache;
68  }
69  if (manifest.isUsingDeveloperTool) {
70    return EXCachedResourceNoCache;
71  }
72  return EXCachedResourceWriteToCache;
73}
74
75@end
76
77NS_ASSUME_NONNULL_END
78
79