1*19a0af8dSWill Schurman// Copyright 2020-present 650 Industries. All rights reserved. 2*19a0af8dSWill Schurman 3*19a0af8dSWill Schurman#import "EXEmbeddedHomeLoader.h" 4*19a0af8dSWill Schurman#import "EXKernel.h" 5*19a0af8dSWill Schurman 6*19a0af8dSWill Schurman#import <React/RCTUtils.h> 7*19a0af8dSWill Schurman 8*19a0af8dSWill Schurman@import EXManifests; 9*19a0af8dSWill Schurman 10*19a0af8dSWill SchurmanNS_ASSUME_NONNULL_BEGIN 11*19a0af8dSWill Schurman 12*19a0af8dSWill SchurmanNSString * const EXEmbeddedHomeLoaderErrorDomain = @"embedded-home-loader"; 13*19a0af8dSWill Schurman 14*19a0af8dSWill SchurmanNSString *kEXHomeBundleResourceName = @"kernel.ios"; 15*19a0af8dSWill SchurmanNSString *kEXHomeManifestResourceName = @"kernel-manifest"; 16*19a0af8dSWill Schurman 17*19a0af8dSWill Schurmantypedef NS_ENUM(NSInteger, EXEmbeddedHomeLoaderErrorCode) { 18*19a0af8dSWill Schurman EXEmbeddedHomeLoaderErrorCodeUnableToLoadEmbeddedManifest, 19*19a0af8dSWill Schurman EXEmbeddedHomeLoaderErrorCodeUnableToLoadEmbeddedBundle, 20*19a0af8dSWill Schurman}; 21*19a0af8dSWill Schurman 22*19a0af8dSWill Schurman@interface EXEmbeddedHomeLoader () 23*19a0af8dSWill Schurman 24*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) EXManifestsManifest *manifest; 25*19a0af8dSWill Schurman@property (nonatomic, strong, nullable) NSData *bundle; 26*19a0af8dSWill Schurman@property (nonatomic, assign) BOOL isUpToDate; 27*19a0af8dSWill Schurman 28*19a0af8dSWill Schurman@end 29*19a0af8dSWill Schurman 30*19a0af8dSWill Schurman@implementation EXEmbeddedHomeLoader 31*19a0af8dSWill Schurman 32*19a0af8dSWill Schurman- (nonnull instancetype)init { 33*19a0af8dSWill Schurman return self = [super init]; 34*19a0af8dSWill Schurman} 35*19a0af8dSWill Schurman 36*19a0af8dSWill Schurman@synthesize manifest = _manifest; 37*19a0af8dSWill Schurman@synthesize bundle = _bundle; 38*19a0af8dSWill Schurman@synthesize isUpToDate = _isUpToDate; 39*19a0af8dSWill Schurman 40*19a0af8dSWill Schurman#pragma mark - getters and lifecycle 41*19a0af8dSWill Schurman 42*19a0af8dSWill Schurman- (void)_reset 43*19a0af8dSWill Schurman{ 44*19a0af8dSWill Schurman _manifest = nil; 45*19a0af8dSWill Schurman _bundle = nil; 46*19a0af8dSWill Schurman} 47*19a0af8dSWill Schurman 48*19a0af8dSWill Schurman- (EXAppLoaderStatus)status 49*19a0af8dSWill Schurman{ 50*19a0af8dSWill Schurman if (_bundle) { 51*19a0af8dSWill Schurman return kEXAppLoaderStatusHasManifestAndBundle; 52*19a0af8dSWill Schurman } else if (_manifest) { 53*19a0af8dSWill Schurman return kEXAppLoaderStatusHasManifest; 54*19a0af8dSWill Schurman } 55*19a0af8dSWill Schurman return kEXAppLoaderStatusNew; 56*19a0af8dSWill Schurman} 57*19a0af8dSWill Schurman 58*19a0af8dSWill Schurman- (nullable EXManifestsManifest *)manifest 59*19a0af8dSWill Schurman{ 60*19a0af8dSWill Schurman return _manifest; 61*19a0af8dSWill Schurman} 62*19a0af8dSWill Schurman 63*19a0af8dSWill Schurman- (nullable NSData *)bundle 64*19a0af8dSWill Schurman{ 65*19a0af8dSWill Schurman return _bundle; 66*19a0af8dSWill Schurman} 67*19a0af8dSWill Schurman 68*19a0af8dSWill Schurman- (BOOL)supportsBundleReload 69*19a0af8dSWill Schurman{ 70*19a0af8dSWill Schurman return NO; 71*19a0af8dSWill Schurman} 72*19a0af8dSWill Schurman 73*19a0af8dSWill Schurman#pragma mark - public 74*19a0af8dSWill Schurman 75*19a0af8dSWill Schurman- (void)request 76*19a0af8dSWill Schurman{ 77*19a0af8dSWill Schurman [self _reset]; 78*19a0af8dSWill Schurman [self _beginRequest]; 79*19a0af8dSWill Schurman} 80*19a0af8dSWill Schurman 81*19a0af8dSWill Schurman- (void)requestFromCache 82*19a0af8dSWill Schurman{ 83*19a0af8dSWill Schurman [self request]; 84*19a0af8dSWill Schurman} 85*19a0af8dSWill Schurman 86*19a0af8dSWill Schurman#pragma mark - internal 87*19a0af8dSWill Schurman 88*19a0af8dSWill Schurman- (void)_beginRequest 89*19a0af8dSWill Schurman{ 90*19a0af8dSWill Schurman NSString *manifestPath = [[NSBundle mainBundle] pathForResource:kEXHomeManifestResourceName ofType:@"json"]; 91*19a0af8dSWill Schurman if (!manifestPath) { 92*19a0af8dSWill Schurman NSError *error = [NSError errorWithDomain:EXEmbeddedHomeLoaderErrorDomain code:EXEmbeddedHomeLoaderErrorCodeUnableToLoadEmbeddedManifest userInfo:@{ 93*19a0af8dSWill Schurman NSLocalizedDescriptionKey: @"Could not load embedded manifest" 94*19a0af8dSWill Schurman }]; 95*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:error]; 96*19a0af8dSWill Schurman return; 97*19a0af8dSWill Schurman } 98*19a0af8dSWill Schurman 99*19a0af8dSWill Schurman NSError *stringReadError; 100*19a0af8dSWill Schurman NSString *manifestJson = [NSString stringWithContentsOfFile:manifestPath encoding:NSUTF8StringEncoding error:&stringReadError]; 101*19a0af8dSWill Schurman if (stringReadError) { 102*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:stringReadError]; 103*19a0af8dSWill Schurman return; 104*19a0af8dSWill Schurman } 105*19a0af8dSWill Schurman 106*19a0af8dSWill Schurman id manifest = RCTJSONParse(manifestJson, nil); 107*19a0af8dSWill Schurman if (![manifest isKindOfClass:[NSDictionary class]]) { 108*19a0af8dSWill Schurman NSError *error = [NSError errorWithDomain:EXEmbeddedHomeLoaderErrorDomain code:EXEmbeddedHomeLoaderErrorCodeUnableToLoadEmbeddedManifest userInfo:@{ 109*19a0af8dSWill Schurman NSLocalizedDescriptionKey: @"Embedded manifest not valid JSON" 110*19a0af8dSWill Schurman }]; 111*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:error]; 112*19a0af8dSWill Schurman return; 113*19a0af8dSWill Schurman } 114*19a0af8dSWill Schurman 115*19a0af8dSWill Schurman if (!([manifest[@"id"] isEqualToString:@"@exponent/home"])) { 116*19a0af8dSWill Schurman DDLogError(@"Bundled kernel manifest was published with an id other than @exponent/home"); 117*19a0af8dSWill Schurman } 118*19a0af8dSWill Schurman 119*19a0af8dSWill Schurman _manifest = [EXManifestsManifestFactory manifestForManifestJSON:manifest]; 120*19a0af8dSWill Schurman if (self.delegate) { 121*19a0af8dSWill Schurman [self.delegate appLoader:self didLoadOptimisticManifest:_manifest]; 122*19a0af8dSWill Schurman } 123*19a0af8dSWill Schurman 124*19a0af8dSWill Schurman _isUpToDate = true; 125*19a0af8dSWill Schurman 126*19a0af8dSWill Schurman NSString *bundlePath = [[NSBundle mainBundle] pathForResource:kEXHomeBundleResourceName ofType:@"bundle"]; 127*19a0af8dSWill Schurman if (!bundlePath) { 128*19a0af8dSWill Schurman NSError *error = [NSError errorWithDomain:EXEmbeddedHomeLoaderErrorDomain code:EXEmbeddedHomeLoaderErrorCodeUnableToLoadEmbeddedManifest userInfo:@{ 129*19a0af8dSWill Schurman NSLocalizedDescriptionKey: @"Could not load embedded bundle" 130*19a0af8dSWill Schurman }]; 131*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:error]; 132*19a0af8dSWill Schurman return; 133*19a0af8dSWill Schurman } 134*19a0af8dSWill Schurman 135*19a0af8dSWill Schurman NSError *dataReadError; 136*19a0af8dSWill Schurman NSData *bundleData = [NSData dataWithContentsOfFile:bundlePath options:0 error:&dataReadError]; 137*19a0af8dSWill Schurman if (dataReadError) { 138*19a0af8dSWill Schurman [self.delegate appLoader:self didFailWithError:dataReadError]; 139*19a0af8dSWill Schurman return; 140*19a0af8dSWill Schurman } 141*19a0af8dSWill Schurman 142*19a0af8dSWill Schurman _bundle = bundleData; 143*19a0af8dSWill Schurman 144*19a0af8dSWill Schurman if (self.delegate) { 145*19a0af8dSWill Schurman [self.delegate appLoader:self didFinishLoadingManifest:_manifest bundle:_bundle]; 146*19a0af8dSWill Schurman } 147*19a0af8dSWill Schurman} 148*19a0af8dSWill Schurman 149*19a0af8dSWill Schurman@end 150*19a0af8dSWill Schurman 151*19a0af8dSWill SchurmanNS_ASSUME_NONNULL_END 152