xref: /expo/ios/Client/EXHomeAppManager.m (revision 9d7b0c19)
1
2#import "EXBuildConstants.h"
3#import "EXHomeAppManager.h"
4#import "EXKernel.h"
5#import "EXAppFetcher.h"
6#import "EXAbstractLoader.h"
7#import "EXKernelLinkingManager.h"
8#import "EXHomeModule.h"
9#import "EXKernelUtil.h"
10#import "EXLog.h"
11#import "ExpoKit.h"
12#import "EXReactAppExceptionHandler.h"
13#import "EXReactAppManager+Private.h"
14#import "EXVersionManager.h"
15#import "EXVersions.h"
16
17#import <EXConstants/EXConstantsService.h>
18
19#import <React/RCTUtils.h>
20#import <React/RCTBridge.h>
21
22#import <ExpoModulesCore/EXModuleRegistryProvider.h>
23
24@import EXManifests;
25@import EXUpdates;
26
27NSString * const kEXHomeLaunchUrlDefaultsKey = @"EXKernelLaunchUrlDefaultsKey";
28NSString *kEXHomeBundleResourceName = @"kernel.ios";
29NSString *kEXHomeManifestResourceName = @"kernel-manifest";
30
31@implementation EXHomeAppManager
32
33- (NSDictionary *)extraParams
34{
35  NSMutableDictionary *params = [@{
36    @"browserModuleClass": [EXHomeModule class],
37    @"constants": @{
38        @"expoRuntimeVersion": [EXBuildConstants sharedInstance].expoRuntimeVersion,
39        @"linkingUri": @"exp://",
40        @"experienceUrl": [@"exp://" stringByAppendingString:self.appRecord.appLoader.manifest.hostUri],
41        @"manifest": self.appRecord.appLoader.manifest.rawManifestJSON,
42        @"executionEnvironment": EXConstantsExecutionEnvironmentStoreClient,
43        @"appOwnership": @"expo",
44        @"supportedExpoSdks": [EXVersions sharedInstance].versions[@"sdkVersions"],
45    },
46    @"exceptionsManagerDelegate": self.exceptionHandler,
47    @"isDeveloper": @([EXBuildConstants sharedInstance].isDevKernel),
48    @"isStandardDevMenuAllowed": @(YES), // kernel enables traditional RN dev menu
49    @"manifest": self.appRecord.appLoader.manifest.rawManifestJSON,
50    @"services": [EXKernel sharedInstance].serviceRegistry.allServices,
51    @"singletonModules": [EXModuleRegistryProvider singletonModules],
52    @"fileSystemDirectories": @{
53        @"documentDirectory": [self scopedDocumentDirectory],
54        @"cachesDirectory": [self scopedCachesDirectory]
55    }
56  } mutableCopy];
57
58  NSURL *initialHomeUrl = [self _initialHomeUrl];
59  if (initialHomeUrl) {
60    params[@"initialUri"] = initialHomeUrl;
61  }
62  return params;
63}
64
65#pragma mark - interfacing with home JS
66
67- (void)addHistoryItemWithUrl:(NSURL *)manifestUrl manifest:(EXManifestsManifest *)manifest
68{
69  if (!manifest || !manifestUrl || [manifest.legacyId isEqualToString:@"@exponent/home"] || [manifest.legacyId isEqualToString:@"@expo-dogfooding/home"]) {
70    return;
71  }
72  NSDictionary *params = @{
73    @"manifestUrl": manifestUrl.absoluteString,
74    @"manifest": manifest.rawManifestJSON,
75  };
76  [self _dispatchHomeJSEvent:@"addHistoryItem" body:params onSuccess:nil onFailure:nil];
77}
78
79- (void)getHistoryUrlForScopeKey:(NSString *)scopeKey completion:(void (^)(NSString *))completion
80{
81  [self _dispatchHomeJSEvent:@"getHistoryUrlForExperienceId"
82                        body:@{ @"experienceId": scopeKey }
83                   onSuccess:^(NSDictionary *result) {
84                     NSString *url = result[@"url"];
85                     completion(url);
86                   } onFailure:^(NSString *errorMessage) {
87                     completion(nil);
88                   }];
89}
90
91- (void)showQRReader
92{
93  [self _dispatchHomeJSEvent:@"showQRReader" body:@{} onSuccess:nil onFailure:nil];
94}
95
96#pragma mark - EXReactAppManager
97
98- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge
99{
100  NSMutableArray *modules = [NSMutableArray array];
101
102  [modules addObjectsFromArray:[self.versionManager extraModulesForBridge:bridge]];
103
104  return modules;
105}
106
107- (void)computeVersionSymbolPrefix
108{
109  self.validatedVersion = nil;
110  self.versionSymbolPrefix = [[EXVersions sharedInstance] symbolPrefixForSdkVersion:self.validatedVersion isKernel:YES];
111}
112
113- (RCTLogFunction)logFunction
114{
115  return EXGetKernelRCTLogFunction();
116}
117
118- (RCTLogLevel)logLevel
119{
120  return RCTLogLevelWarning;
121}
122
123- (NSDictionary *)launchOptionsForBridge
124{
125  if (!self.hasBridgeEverLoaded) {
126    return [ExpoKit sharedInstance].launchOptions;
127  } else {
128    // don't want to re-consume launch options when the bridge reloads.
129    return nil;
130  }
131}
132
133- (NSString *)bundleResourceNameForAppFetcher:(__unused EXAppFetcher *)appFetcher withManifest:(nonnull __unused EXManifestsManifest *)manifest
134{
135  return kEXHomeBundleResourceName;
136}
137
138#pragma mark - util
139
140- (void)_dispatchHomeJSEvent:(NSString *)eventName body:(NSDictionary *)eventBody onSuccess:(void (^_Nullable)(NSDictionary * _Nullable))success onFailure:(void (^_Nullable)(NSString * _Nullable))failure
141{
142  EXHomeModule *homeModule = [[EXKernel sharedInstance] nativeModuleForAppManager:self named:@"ExponentKernel"];
143  if (homeModule) {
144    [homeModule dispatchJSEvent:eventName body:eventBody onSuccess:success onFailure:failure];
145  } else {
146    if (failure) {
147      failure(nil);
148    }
149  }
150}
151
152- (NSURL *)_initialHomeUrl
153{
154  // used by appetize - override the kernel initial url if there's something in NSUserDefaults
155  NSURL *initialHomeUrl;
156  NSString *kernelInitialUrlDefaultsValue = [[NSUserDefaults standardUserDefaults] stringForKey:kEXHomeLaunchUrlDefaultsKey];
157  if (kernelInitialUrlDefaultsValue) {
158    initialHomeUrl = [NSURL URLWithString:kernelInitialUrlDefaultsValue];
159    [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXHomeLaunchUrlDefaultsKey];
160    [[NSUserDefaults standardUserDefaults] synchronize];
161  } else {
162    initialHomeUrl = [EXKernelLinkingManager initialUrlFromLaunchOptions:[self launchOptionsForBridge]];
163  }
164  return initialHomeUrl;
165}
166
167+ (EXManifestsManifest * _Nullable)bundledHomeManifest
168{
169  NSString *manifestJson = nil;
170  BOOL usesNSBundleManifest = NO;
171
172  // if developing, use development manifest from EXBuildConstants
173  if ([EXBuildConstants sharedInstance].isDevKernel) {
174    manifestJson = [EXBuildConstants sharedInstance].kernelManifestJsonString;
175  }
176
177  // otherwise use published manifest
178  if (!manifestJson) {
179    NSString *manifestPath = [[NSBundle mainBundle] pathForResource:kEXHomeManifestResourceName ofType:@"json"];
180    if (manifestPath) {
181      NSError *error;
182      usesNSBundleManifest = YES;
183      manifestJson = [NSString stringWithContentsOfFile:manifestPath encoding:NSUTF8StringEncoding error:&error];
184      if (error) {
185        manifestJson = nil;
186      }
187    }
188  }
189
190  if (manifestJson) {
191    id manifest = RCTJSONParse(manifestJson, nil);
192    if ([manifest isKindOfClass:[NSDictionary class]]) {
193      if (usesNSBundleManifest && !([manifest[@"id"] isEqualToString:@"@exponent/home"] || [manifest[@"id"] isEqualToString:@"@expo-dogfooding/home"])) {
194        DDLogError(@"Bundled kernel manifest was published with an id other than @exponent/home or @expo-dogfooding/home");
195      }
196      return [EXManifestsManifestFactory manifestForManifestJSON:manifest];
197    }
198  }
199  return nil;
200}
201
202- (BOOL)requiresValidManifests
203{
204  // running home
205  return NO;
206}
207
208@end
209