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