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