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