xref: /expo/ios/Client/EXHomeAppManager.m (revision b0932293)
1
2#import "EXBuildConstants.h"
3#import "EXHomeAppManager.h"
4#import "EXKernel.h"
5#import "EXKernelAppLoader.h"
6#import "EXKernelLinkingManager.h"
7#import "EXHomeModule.h"
8#import "EXKernelUtil.h"
9#import "EXLog.h"
10#import "ExpoKit.h"
11#import "EXReactAppExceptionHandler.h"
12#import "EXReactAppManager+Private.h"
13#import "EXVersionManager.h"
14#import "EXVersions.h"
15
16#import <React/RCTUtils.h>
17#import <React/RCTBridge.h>
18
19NSString * const kEXHomeLaunchUrlDefaultsKey = @"EXKernelLaunchUrlDefaultsKey";
20NSString *kEXHomeBundleResourceName = @"kernel.ios";
21NSString *kEXHomeManifestResourceName = @"kernel-manifest";
22
23@implementation EXHomeAppManager
24
25#pragma mark - interfacing with home JS
26
27- (void)addHistoryItemWithUrl:(NSURL *)manifestUrl manifest:(NSDictionary *)manifest
28{
29  if (!manifest || !manifestUrl || [manifest[@"id"] isEqualToString:@"@exponent/home"]) {
30    return;
31  }
32  NSDictionary *params = @{
33    @"manifestUrl": manifestUrl.absoluteString,
34    @"manifest": manifest,
35  };
36  [self _dispatchHomeJSEvent:@"addHistoryItem" body:params onSuccess:nil onFailure:nil];
37}
38
39- (void)getHistoryUrlForExperienceId:(NSString *)experienceId completion:(void (^)(NSString *))completion
40{
41  [self _dispatchHomeJSEvent:@"getHistoryUrlForExperienceId"
42                        body:@{ @"experienceId": experienceId }
43                   onSuccess:^(NSDictionary *result) {
44                     NSString *url = result[@"url"];
45                     completion(url);
46                   } onFailure:^(NSString *errorMessage) {
47                     completion(nil);
48                   }];
49}
50
51- (void)showQRReader
52{
53  [self _dispatchHomeJSEvent:@"showQRReader" body:@{} onSuccess:nil onFailure:nil];
54}
55
56#pragma mark - EXReactAppManager
57
58- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge
59{
60  NSMutableArray *modules = [NSMutableArray array];
61  self.exceptionHandler = [[EXReactAppExceptionHandler alloc] initWithAppRecord:self.appRecord];
62
63  // TODO: ben: common params impl?
64  NSMutableDictionary *params = [@{
65                                   @"constants": @{
66                                       @"deviceId": [EXKernel deviceInstallUUID],
67                                       @"expoRuntimeVersion": [EXBuildConstants sharedInstance].expoRuntimeVersion,
68                                       @"linkingUri": @"exp://",
69                                       @"manifest": self.appRecord.appLoader.manifest,
70                                       @"appOwnership": @"expo",
71                                     },
72                                   @"exceptionsManagerDelegate": self.exceptionHandler,
73                                   @"kernel": [EXKernel sharedInstance],
74                                   @"supportedSdkVersions": [EXVersions sharedInstance].versions[@"sdkVersions"],
75                                   @"isDeveloper": @([EXBuildConstants sharedInstance].isDevKernel),
76                                   @"isStandardDevMenuAllowed": @(YES), // kernel enables traditional RN dev menu
77                                   @"manifest": self.appRecord.appLoader.manifest,
78                                   @"services": [EXKernel sharedInstance].serviceRegistry.allServices,
79                                   } mutableCopy];
80
81
82  NSURL *initialHomeUrl = [self _initialHomeUrl];
83  if (initialHomeUrl) {
84    params[@"initialUri"] = initialHomeUrl;
85  }
86
87  [modules addObjectsFromArray:[self.versionManager extraModulesWithParams:params]];
88
89  return modules;
90}
91
92- (void)computeVersionSymbolPrefix
93{
94  /* TODO: BEN: kill me
95   NSDictionary *detachedVersions = [EXVersions sharedInstance].versions[@"detachedNativeVersions"];
96  if (detachedVersions) {
97    self.validatedVersion = detachedVersions[@"kernel"];
98  } else {
99    self.validatedVersion = nil;
100  } */
101  self.validatedVersion = nil;
102  self.versionSymbolPrefix = [[EXVersions sharedInstance] symbolPrefixForSdkVersion:self.validatedVersion isKernel:YES];
103}
104
105- (RCTLogFunction)logFunction
106{
107  return EXGetKernelRCTLogFunction();
108}
109
110- (RCTLogLevel)logLevel
111{
112  return RCTLogLevelWarning;
113}
114
115- (NSDictionary *)launchOptionsForBridge
116{
117  if (!self.hasBridgeEverLoaded) {
118    return [ExpoKit sharedInstance].launchOptions;
119  } else {
120    // don't want to re-consume launch options when the bridge reloads.
121    return nil;
122  }
123}
124
125#pragma mark - util
126
127- (void)_dispatchHomeJSEvent:(NSString *)eventName body:(NSDictionary *)eventBody onSuccess:(void (^_Nullable)(NSDictionary * _Nullable))success onFailure:(void (^_Nullable)(NSString * _Nullable))failure
128{
129  EXHomeModule *homeModule = [[EXKernel sharedInstance] nativeModuleForAppManager:self named:@"ExponentKernel"];
130  if (homeModule) {
131    [homeModule dispatchJSEvent:eventName body:eventBody onSuccess:success onFailure:failure];
132  } else {
133    if (failure) {
134      failure(nil);
135    }
136  }
137}
138
139- (NSURL *)_initialHomeUrl
140{
141  // used by appetize - override the kernel initial url if there's something in NSUserDefaults
142  NSURL *initialHomeUrl;
143  NSString *kernelInitialUrlDefaultsValue = [[NSUserDefaults standardUserDefaults] stringForKey:kEXHomeLaunchUrlDefaultsKey];
144  if (kernelInitialUrlDefaultsValue) {
145    initialHomeUrl = [NSURL URLWithString:kernelInitialUrlDefaultsValue];
146    [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXHomeLaunchUrlDefaultsKey];
147    [[NSUserDefaults standardUserDefaults] synchronize];
148  } else {
149    initialHomeUrl = [EXKernelLinkingManager initialUrlFromLaunchOptions:[self launchOptionsForBridge]];
150  }
151  return initialHomeUrl;
152}
153
154+ (NSDictionary * _Nullable)bundledHomeManifest
155{
156  NSString *manifestJson = nil;
157  BOOL usesNSBundleManifest = NO;
158
159  // if developing, use development manifest from EXBuildConstants
160  if ([EXBuildConstants sharedInstance].isDevKernel) {
161    manifestJson = [EXBuildConstants sharedInstance].kernelManifestJsonString;
162  }
163
164  // otherwise use published manifest
165  if (!manifestJson) {
166    NSString *manifestPath = [[NSBundle mainBundle] pathForResource:kEXHomeManifestResourceName ofType:@"json"];
167    if (manifestPath) {
168      NSError *error;
169      usesNSBundleManifest = YES;
170      manifestJson = [NSString stringWithContentsOfFile:manifestPath encoding:NSUTF8StringEncoding error:&error];
171      if (error) {
172        manifestJson = nil;
173      }
174    }
175  }
176
177  if (manifestJson) {
178    id manifest = RCTJSONParse(manifestJson, nil);
179    if ([manifest isKindOfClass:[NSDictionary class]]) {
180      if (usesNSBundleManifest && ![manifest[@"id"] isEqualToString:@"@exponent/home"]) {
181        DDLogError(@"Bundled kernel manifest was published with an id other than @exponent/home");
182      }
183      return manifest;
184    }
185  }
186  return nil;
187}
188
189// TODO: BEN: here's some other things that were different about the old kernel manager
190
191/*
192- (NSString *)bundleNameForJSResource
193{
194  return kEXKernelBundleResourceName;
195}
196
197- (EXCachedResourceBehavior)cacheBehaviorForJSResource
198{
199  if ([EXBuildConstants sharedInstance].isDevKernel) {
200    // to prevent running dev native code against prod js.
201    return EXCachedResourceNoCache;
202  } else {
203    return [[NSUserDefaults standardUserDefaults] boolForKey:kEXSkipCacheUserDefaultsKey] ?
204    EXCachedResourceNoCache :
205    EXCachedResourceUseCacheImmediately;
206  }
207}
208
209 // TODO: ben: restore
210 // this happened before the js resource download call:
211 if ([self shouldInvalidateJSResourceCache]) {
212   [_jsResource removeCache];
213 }
214- (BOOL)shouldInvalidateJSResourceCache
215{
216  // if crashlytics shows that we're recovering from a native crash, invalidate any downloaded kernel cache.
217  BOOL shouldClearKernelCache = [[NSUserDefaults standardUserDefaults] boolForKey:kEXKernelClearJSCacheUserDefaultsKey];
218  if (shouldClearKernelCache) {
219    [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXKernelClearJSCacheUserDefaultsKey];
220    return YES;
221  }
222  return NO;
223}
224
225*/
226
227@end
228