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:(NSDictionary *)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:[[self class] areDevToolsEnabledWithManifest:manifest]]; 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+ (NSString *)experienceIdWithManifest:(NSDictionary * _Nonnull)manifest 59{ 60 id experienceIdJsonValue = manifest[@"id"]; 61 if (experienceIdJsonValue) { 62 RCTAssert([experienceIdJsonValue isKindOfClass:[NSString class]], @"Manifest contains an id which is not a string: %@", experienceIdJsonValue); 63 return experienceIdJsonValue; 64 } 65 return nil; 66} 67 68+ (BOOL)areDevToolsEnabledWithManifest:(NSDictionary * _Nonnull)manifest 69{ 70 NSDictionary *manifestDeveloperConfig = manifest[@"developer"]; 71 BOOL isDeployedFromTool = (manifestDeveloperConfig && manifestDeveloperConfig[@"tool"] != nil); 72 return (isDeployedFromTool); 73} 74 75+ (EXCachedResourceBehavior)cacheBehaviorForJSWithManifest:(NSDictionary * _Nonnull)manifest 76{ 77 if ([[EXKernel sharedInstance].serviceRegistry.errorRecoveryManager experienceIdIsRecoveringFromError:[[self class] experienceIdWithManifest:manifest]]) { 78 // if this experience id encountered a loading error before, discard any cache we might have 79 return EXCachedResourceWriteToCache; 80 } 81 if ([[self class] areDevToolsEnabledWithManifest:manifest]) { 82 return EXCachedResourceNoCache; 83 } 84 return EXCachedResourceWriteToCache; 85} 86 87@end 88 89NS_ASSUME_NONNULL_END 90 91