xref: /expo/ios/Client/EXRootViewController.m (revision 5d8188f5)
1// Copyright 2015-present 650 Industries. All rights reserved.
2
3@import UIKit;
4
5#import "EXAppDelegate.h"
6#import "EXAppViewController.h"
7#import "EXHomeAppManager.h"
8#import "EXHomeDiagnosticsViewController.h"
9#import "EXKernel.h"
10#import "EXKernelAppLoader.h"
11#import "EXKernelAppRecord.h"
12#import "EXKernelAppRegistry.h"
13#import "EXKernelLinkingManager.h"
14#import "EXKernelServiceRegistry.h"
15#import "EXMenuViewController.h"
16#import "EXRootViewController.h"
17
18NSString * const kEXHomeDisableNuxDefaultsKey = @"EXKernelDisableNuxDefaultsKey";
19NSString * const kEXHomeIsNuxFinishedDefaultsKey = @"EXHomeIsNuxFinishedDefaultsKey";
20
21NS_ASSUME_NONNULL_BEGIN
22
23@interface EXRootViewController () <EXAppBrowserController>
24
25@property (nonatomic, strong) EXMenuViewController *menuViewController;
26@property (nonatomic, assign) BOOL isMenuVisible;
27
28@end
29
30@implementation EXRootViewController
31
32- (instancetype)init
33{
34  if (self = [super init]) {
35    [EXKernel sharedInstance].browserController = self;
36    [self _maybeResetNuxState];
37  }
38  return self;
39}
40
41#pragma mark - EXViewController
42
43- (void)createRootAppAndMakeVisible
44{
45  EXHomeAppManager *homeAppManager = [[EXHomeAppManager alloc] init];
46  EXKernelAppLoader *homeAppLoader = [[EXKernelAppLoader alloc] initWithLocalManifest:[EXHomeAppManager bundledHomeManifest]];
47  EXKernelAppRecord *homeAppRecord = [[EXKernelAppRecord alloc] initWithAppLoader:homeAppLoader appManager:homeAppManager];
48  [[EXKernel sharedInstance].appRegistry registerHomeAppRecord:homeAppRecord];
49  [self moveAppToVisible:homeAppRecord];
50}
51
52#pragma mark - EXAppBrowserController
53
54- (void)moveAppToVisible:(EXKernelAppRecord *)appRecord
55{
56  [self _foregroundAppRecord:appRecord];
57}
58
59- (void)toggleMenu
60{
61  [self setIsMenuVisible:!_isMenuVisible];
62}
63
64- (void)setIsMenuVisible:(BOOL)isMenuVisible
65{
66  if (!_menuViewController) {
67    _menuViewController = [[EXMenuViewController alloc] init];
68  }
69
70  // TODO: ben: can this be more robust?
71  // some third party libs (and core RN) often just look for the root VC and present random crap from it.
72  if (self.presentedViewController && self.presentedViewController != _menuViewController) {
73    [self.presentedViewController dismissViewControllerAnimated:NO completion:nil];
74  }
75
76  if (isMenuVisible != _isMenuVisible) {
77    _isMenuVisible = isMenuVisible;
78    if (_isMenuVisible) {
79      [self presentViewController:_menuViewController animated:NO completion:nil];
80    } else {
81      [_menuViewController dismissViewControllerAnimated:NO completion:nil];
82    }
83  }
84}
85
86- (void)showDiagnostics
87{
88  EXHomeDiagnosticsViewController *vcDiagnostics = [[EXHomeDiagnosticsViewController alloc] init];
89  [self setIsMenuVisible:NO];
90  [self presentViewController:vcDiagnostics animated:NO completion:nil];
91}
92
93- (void)showQRReader
94{
95  [self moveHomeToVisible];
96  [[self _getHomeAppManager] showQRReader];
97}
98
99- (void)moveHomeToVisible
100{
101  [self setIsMenuVisible:NO];
102  [[EXKernel sharedInstance] moveAppToVisible:[EXKernel sharedInstance].appRegistry.homeAppRecord];
103}
104
105- (void)refreshVisibleApp
106{
107  [self setIsMenuVisible:NO];
108  NSURL *urlToRefresh = [EXKernel sharedInstance].visibleApp.appLoader.manifestUrl;
109  [[EXKernel sharedInstance] createNewAppWithUrl:urlToRefresh initialProps:nil];
110}
111
112- (void)addHistoryItemWithUrl:(NSURL *)manifestUrl manifest:(NSDictionary *)manifest
113{
114  [[self _getHomeAppManager] addHistoryItemWithUrl:manifestUrl manifest:manifest];
115}
116
117- (void)getHistoryUrlForExperienceId:(NSString *)experienceId completion:(void (^)(NSString *))completion
118{
119  return [[self _getHomeAppManager] getHistoryUrlForExperienceId:experienceId completion:completion];
120}
121
122- (void)setIsNuxFinished:(BOOL)isFinished
123{
124  [[NSUserDefaults standardUserDefaults] setBool:isFinished forKey:kEXHomeIsNuxFinishedDefaultsKey];
125  [[NSUserDefaults standardUserDefaults] synchronize];
126}
127
128- (BOOL)isNuxFinished
129{
130  return [[NSUserDefaults standardUserDefaults] boolForKey:kEXHomeIsNuxFinishedDefaultsKey];
131}
132
133- (void)appDidFinishLoadingSuccessfully:(EXKernelAppRecord *)appRecord
134{
135  // show nux if needed
136  if (!self.isNuxFinished
137      && appRecord == [EXKernel sharedInstance].visibleApp
138      && appRecord != [EXKernel sharedInstance].appRegistry.homeAppRecord
139      && !self.isMenuVisible) {
140    [self setIsMenuVisible:YES];
141  }
142}
143
144#pragma mark - internal
145
146- (void)_foregroundAppRecord:(EXKernelAppRecord *)appRecord
147{
148  UIViewController *viewControllerToShow = appRecord.viewController;
149  if (viewControllerToShow != self.contentViewController) {
150    if (self.contentViewController) {
151      [self.contentViewController willMoveToParentViewController:nil];
152      [self.contentViewController.view removeFromSuperview];
153      [self.contentViewController didMoveToParentViewController:nil];
154    }
155
156    if (viewControllerToShow) {
157      [viewControllerToShow willMoveToParentViewController:self];
158      [self.view addSubview:viewControllerToShow.view];
159      [viewControllerToShow didMoveToParentViewController:self];
160    }
161
162    self.contentViewController = viewControllerToShow;
163    [self.view setNeedsLayout];
164    [[EXKernel sharedInstance] appDidBecomeVisible:appRecord];
165  }
166}
167
168- (EXHomeAppManager *)_getHomeAppManager
169{
170  return (EXHomeAppManager *)[EXKernel sharedInstance].appRegistry.homeAppRecord.appManager;
171}
172
173- (void)_maybeResetNuxState
174{
175  // used by appetize: optionally disable nux
176  BOOL disableNuxDefaultsValue = [[NSUserDefaults standardUserDefaults] boolForKey:kEXHomeDisableNuxDefaultsKey];
177  if (disableNuxDefaultsValue) {
178    [self setIsNuxFinished:YES];
179    [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXHomeDisableNuxDefaultsKey];
180  }
181}
182
183@end
184
185NS_ASSUME_NONNULL_END
186