Browse Source

Fixes detached overlays on setRoot (#4204)

Yogev Ben David 5 years ago
parent
commit
7811baa00a
No account linked to committer's email address

+ 7
- 2
lib/ios/RNNBridgeManager.m View File

@@ -23,6 +23,7 @@
23 23
 	RCTBridge* _bridge;
24 24
 
25 25
 	RNNStore* _store;
26
+	RNNStore* _overlayStore;
26 27
 
27 28
 	RNNCommandsHandler* _commandsHandler;
28 29
 }
@@ -34,6 +35,7 @@
34 35
 		_delegate = delegate;
35 36
 		
36 37
 		_store = [RNNStore new];
38
+		_overlayStore = [RNNStore new];
37 39
 		_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:_launchOptions];
38 40
 
39 41
 		[[NSNotificationCenter defaultCenter] addObserver:self
@@ -74,8 +76,9 @@
74 76
 	RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init];
75 77
 
76 78
 	id<RNNRootViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
77
-	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator store:_store eventEmitter:eventEmitter andBridge:bridge];
78
-	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory eventEmitter:eventEmitter];
79
+	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter andBridge:bridge];
80
+	
81
+	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store overlayStore:_overlayStore controllerFactory:controllerFactory eventEmitter:eventEmitter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:[RNNOverlayManager new]];
79 82
 	RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler];
80 83
 
81 84
 	return [@[bridgeModule,eventEmitter] arrayByAddingObjectsFromArray:[self extraModulesFromDelegate]];
@@ -85,10 +88,12 @@
85 88
 
86 89
 - (void)onJavaScriptWillLoad {
87 90
 	[_store clean];
91
+	[_overlayStore clean];
88 92
 }
89 93
 
90 94
 - (void)onJavaScriptLoaded {
91 95
 	[_store setReadyToReceiveCommands:true];
96
+	[_overlayStore setReadyToReceiveCommands:true];
92 97
 	[[_bridge moduleForClass:[RNNEventEmitter class]] sendOnAppLaunched];
93 98
 }
94 99
 

+ 4
- 1
lib/ios/RNNCommandsHandler.h View File

@@ -3,10 +3,13 @@
3 3
 
4 4
 #import "RNNControllerFactory.h"
5 5
 #import "RNNStore.h"
6
+#import "RNNModalManager.h"
7
+#import "RNNNavigationStackManager.h"
8
+#import "RNNOverlayManager.h"
6 9
 
7 10
 @interface RNNCommandsHandler : NSObject
8 11
 
9
-- (instancetype)initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter*)eventEmitter;
12
+- (instancetype)initWithStore:(RNNStore*)store overlayStore:(RNNStore*)overlayStore controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager;
10 13
 
11 14
 - (void)setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion;
12 15
 

+ 12
- 13
lib/ios/RNNCommandsHandler.m View File

@@ -1,7 +1,4 @@
1 1
 #import "RNNCommandsHandler.h"
2
-#import "RNNModalManager.h"
3
-#import "RNNNavigationStackManager.h"
4
-#import "RNNOverlayManager.h"
5 2
 #import "RNNNavigationOptions.h"
6 3
 #import "RNNRootViewController.h"
7 4
 #import "RNNSplitViewController.h"
@@ -33,21 +30,23 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
33 30
 @implementation RNNCommandsHandler {
34 31
 	RNNControllerFactory *_controllerFactory;
35 32
 	RNNStore *_store;
33
+	RNNStore *_overlayStore;
36 34
 	RNNModalManager* _modalManager;
37 35
 	RNNOverlayManager* _overlayManager;
38 36
 	RNNNavigationStackManager* _stackManager;
39 37
 	RNNEventEmitter* _eventEmitter;
40 38
 }
41 39
 
42
-- (instancetype)initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter {
40
+- (instancetype)initWithStore:(RNNStore*)store overlayStore:(RNNStore*)overlayStore controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager {
43 41
 	self = [super init];
44 42
 	_store = store;
43
+	_overlayStore = overlayStore;
45 44
 	_controllerFactory = controllerFactory;
46 45
 	_eventEmitter = eventEmitter;
47
-	_modalManager = [[RNNModalManager alloc] init];
46
+	_modalManager = modalManager;
48 47
 	_modalManager.delegate = self;
49
-	_stackManager = [[RNNNavigationStackManager alloc] init];
50
-	_overlayManager = [[RNNOverlayManager alloc] init];
48
+	_stackManager = stackManager;
49
+	_overlayManager = overlayManager;
51 50
 	return self;
52 51
 }
53 52
 
@@ -59,7 +58,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
59 58
 	[_modalManager dismissAllModalsAnimated:NO];
60 59
 	[_store removeAllComponents];
61 60
 	
62
-	UIViewController *vc = [_controllerFactory createLayoutAndSaveToStore:layout[@"root"]];
61
+	UIViewController *vc = [_controllerFactory createLayout:layout[@"root"] saveToStore:_store];
63 62
 	
64 63
 	UIApplication.sharedApplication.delegate.window.rootViewController = vc;
65 64
 	[UIApplication.sharedApplication.delegate.window makeKeyWindow];
@@ -97,7 +96,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
97 96
 - (void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
98 97
 	[self assertReady];
99 98
 	
100
-	UIViewController<RNNLayoutProtocol> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
99
+	UIViewController<RNNLayoutProtocol> *newVc = [_controllerFactory createLayout:layout saveToStore:_store];
101 100
 	UIViewController *fromVC = [_store findComponentForId:componentId];
102 101
 	
103 102
 	if ([newVc.options.preview.reactTag floatValue] > 0) {
@@ -154,7 +153,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
154 153
 - (void)setStackRoot:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
155 154
 	[self assertReady];
156 155
 	
157
-	UIViewController<RNNParentProtocol> *newVC = [_controllerFactory createLayoutAndSaveToStore:layout];
156
+	UIViewController<RNNParentProtocol> *newVC = [_controllerFactory createLayout:layout saveToStore:_store];
158 157
 	RNNNavigationOptions* options = [newVC getCurrentChild].options;
159 158
 	UIViewController *fromVC = [_store findComponentForId:componentId];
160 159
 	__weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter;
@@ -229,7 +228,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
229 228
 - (void)showModal:(NSDictionary*)layout completion:(RNNTransitionWithComponentIdCompletionBlock)completion {
230 229
 	[self assertReady];
231 230
 	
232
-	UIViewController<RNNParentProtocol> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
231
+	UIViewController<RNNParentProtocol> *newVc = [_controllerFactory createLayout:layout saveToStore:_store];
233 232
 	
234 233
 	[newVc.getCurrentChild waitForReactViewRender:newVc.getCurrentChild.options.animations.showModal.waitForRender perform:^{
235 234
 		[_modalManager showModal:newVc animated:newVc.getCurrentChild.options.animations.showModal.enable hasCustomAnimation:newVc.getCurrentChild.options.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) {
@@ -281,7 +280,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
281 280
 - (void)showOverlay:(NSDictionary *)layout completion:(RNNTransitionCompletionBlock)completion {
282 281
 	[self assertReady];
283 282
 	
284
-	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayoutAndSaveToStore:layout];
283
+	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayout:layout saveToStore:_overlayStore];
285 284
 	[_overlayManager showOverlay:overlayVC];
286 285
 	[_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}];
287 286
 	completion();
@@ -289,7 +288,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
289 288
 
290 289
 - (void)dismissOverlay:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {
291 290
 	[self assertReady];
292
-	UIViewController* viewController = [_store findComponentForId:componentId];
291
+	UIViewController* viewController = [_overlayStore findComponentForId:componentId];
293 292
 	if (viewController) {
294 293
 		[_overlayManager dismissOverlay:viewController];
295 294
 		[_eventEmitter sendOnNavigationCommandCompletion:dismissOverlay params:@{@"componentId": componentId}];

+ 1
- 2
lib/ios/RNNControllerFactory.h View File

@@ -9,11 +9,10 @@
9 9
 @interface RNNControllerFactory : NSObject
10 10
 
11 11
 -(instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
12
-								 store:(RNNStore*)store
13 12
 						  eventEmitter:(RNNEventEmitter*)eventEmitter
14 13
 							 andBridge:(RCTBridge*)bridge;
15 14
 
16
--(UIViewController<RNNParentProtocol, UIViewControllerPreviewingDelegate> *)createLayoutAndSaveToStore:(NSDictionary*)layout;
15
+- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store;
17 16
 
18 17
 @property (nonatomic, strong) RNNEventEmitter *eventEmitter;
19 18
 

+ 5
- 4
lib/ios/RNNControllerFactory.m View File

@@ -25,22 +25,23 @@
25 25
 
26 26
 
27 27
 - (instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
28
-								  store:(RNNStore *)store
29 28
 						   eventEmitter:(RNNEventEmitter*)eventEmitter
30 29
 							  andBridge:(RCTBridge *)bridge {
31 30
 	
32 31
 	self = [super init];
33 32
 	
34 33
 	_creator = creator;
35
-	_store = store;
36 34
 	_eventEmitter = eventEmitter;
37 35
 	_bridge = bridge;
38 36
 	
39 37
 	return self;
40 38
 }
41 39
 
42
-- (UIViewController<RNNParentProtocol> *)createLayoutAndSaveToStore:(NSDictionary*)layout {
43
-	return [self fromTree:layout];
40
+- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store {
41
+	_store = store;
42
+	UIViewController<RNNParentProtocol>* layoutViewController = [self fromTree:layout];
43
+	_store = nil;
44
+	return layoutViewController;
44 45
 }
45 46
 
46 47
 # pragma mark private

+ 0
- 7
lib/ios/RNNOverlayOptions.m View File

@@ -11,11 +11,4 @@
11 11
 	return self;
12 12
 }
13 13
 
14
-- (void)applyOn:(UIViewController *)viewController {
15
-//	if (self.interceptTouchOutside) {
16
-//		RCTRootView* rootView = (RCTRootView*)viewController.view;
17
-//		rootView.passThroughTouches = ![self.interceptTouchOutside boolValue];
18
-//	}
19
-}
20
-
21 14
 @end

+ 7
- 0
lib/ios/RNNViewControllerPresenter.m View File

@@ -3,6 +3,7 @@
3 3
 #import "UITabBarController+RNNOptions.h"
4 4
 #import "RNNNavigationButtons.h"
5 5
 #import "RCTConvert+Modal.h"
6
+#import "RNNReactView.h"
6 7
 
7 8
 @interface RNNViewControllerPresenter()
8 9
 @property (nonatomic, strong) RNNNavigationButtons* navigationButtons;
@@ -23,6 +24,7 @@
23 24
 	[viewController rnn_setStatusBarBlur:[options.statusBar.blur getWithDefaultValue:NO]];
24 25
 	[viewController rnn_setStatusBarStyle:[options.statusBar.style getWithDefaultValue:@"default"] animated:[options.statusBar.animate getWithDefaultValue:YES]];
25 26
 	[viewController rnn_setBackButtonVisible:[options.topBar.backButton.visible getWithDefaultValue:YES]];
27
+	[viewController rnn_setInterceptTouchOutside:[options.overlay.interceptTouchOutside getWithDefaultValue:YES]];
26 28
 
27 29
 	if (options.layout.backgroundColor.hasValue) {
28 30
 		[viewController rnn_setBackgroundColor:options.layout.backgroundColor.get];
@@ -117,6 +119,11 @@
117 119
 		_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:(RNNRootViewController*)viewController];
118 120
 		[_navigationButtons applyLeftButtons:options.topBar.leftButtons rightButtons:options.topBar.rightButtons defaultLeftButtonStyle:buttonsResolvedOptions.topBar.leftButtonStyle defaultRightButtonStyle:buttonsResolvedOptions.topBar.rightButtonStyle];
119 121
 	}
122
+	
123
+	if (options.overlay.interceptTouchOutside.hasValue) {
124
+		RCTRootView* rootView = (RCTRootView*)viewController.view;
125
+		rootView.passThroughTouches = !options.overlay.interceptTouchOutside.get;
126
+	}
120 127
 }
121 128
 
122 129
 @end

+ 96
- 3
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

@@ -4,8 +4,9 @@
4 4
 #import "RNNNavigationOptions.h"
5 5
 #import "RNNTestRootViewCreator.h"
6 6
 #import "RNNRootViewController.h"
7
-#import "RNNNavigationStackManager.h"
8 7
 #import "RNNNavigationController.h"
8
+#import "RNNErrorHandler.h"
9
+#import <OCMock/OCMock.h>
9 10
 
10 11
 @interface MockUINavigationController : RNNNavigationController
11 12
 @property (nonatomic, strong) NSArray* willReturnVCs;
@@ -26,11 +27,15 @@
26 27
 @interface RNNCommandsHandlerTest : XCTestCase
27 28
 
28 29
 @property (nonatomic, strong) RNNStore* store;
30
+@property (nonatomic, strong) id overlayStore;
29 31
 @property (nonatomic, strong) RNNCommandsHandler* uut;
30 32
 @property (nonatomic, strong) RNNRootViewController* vc1;
31 33
 @property (nonatomic, strong) RNNRootViewController* vc2;
32 34
 @property (nonatomic, strong) RNNRootViewController* vc3;
33 35
 @property (nonatomic, strong) MockUINavigationController* nvc;
36
+@property (nonatomic, strong) id controllerFactory;
37
+@property (nonatomic, strong) id overlayManager;
38
+@property (nonatomic, strong) id eventEmmiter;
34 39
 
35 40
 @end
36 41
 
@@ -40,7 +45,11 @@
40 45
 	[super setUp];
41 46
 //	[self.store setReadyToReceiveCommands:true];
42 47
 	self.store = [[RNNStore alloc] init];
43
-	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:[[RNNControllerFactory alloc] initWithRootViewCreator:nil store:self.store eventEmitter:nil andBridge:nil] eventEmitter:nil];
48
+	self.overlayStore = [OCMockObject partialMockForObject:[[RNNStore alloc] init]];
49
+	self.eventEmmiter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
50
+	self.overlayManager = [OCMockObject partialMockForObject:[RNNOverlayManager new]];
51
+	self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter andBridge:nil]];
52
+	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store overlayStore:self.overlayStore controllerFactory:self.controllerFactory eventEmitter:self.eventEmmiter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:self.overlayManager];
44 53
 	self.vc1 = [RNNRootViewController new];
45 54
 	self.vc2 = [RNNRootViewController new];
46 55
 	self.vc3 = [RNNRootViewController new];
@@ -67,7 +76,7 @@
67 76
 -(NSArray*) getPublicMethodNamesForObject:(NSObject*)obj{
68 77
 	NSMutableArray* skipMethods = [NSMutableArray new];
69 78
 
70
-	[skipMethods addObject:@"initWithStore:controllerFactory:eventEmitter:"];
79
+	[skipMethods addObject:@"initWithStore:overlayStore:controllerFactory:eventEmitter:stackManager:modalManager:overlayManager:"];
71 80
 	[skipMethods addObject:@"assertReady"];
72 81
 	[skipMethods addObject:@"removePopedViewControllers:"];
73 82
 	[skipMethods addObject:@".cxx_destruct"];
@@ -184,4 +193,88 @@
184 193
 	[self waitForExpectationsWithTimeout:1 handler:nil];
185 194
 }
186 195
 
196
+- (void)testShowOverlay_createLayout {
197
+	[self.store setReadyToReceiveCommands:true];
198
+	OCMStub([self.overlayManager showOverlay:[OCMArg any]]);
199
+	NSDictionary* layout = @{};
200
+	
201
+	[[self.controllerFactory expect] createLayout:layout saveToStore:self.overlayStore];
202
+	[self.uut showOverlay:layout completion:^{}];
203
+	[self.controllerFactory verify];
204
+}
205
+
206
+- (void)testShowOverlay_saveToOverlayStore {
207
+	[self.store setReadyToReceiveCommands:true];
208
+	OCMStub([self.overlayManager showOverlay:[OCMArg any]]);
209
+	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]);
210
+	
211
+	[[self.controllerFactory expect] createLayout:[OCMArg any] saveToStore:self.overlayStore];
212
+	[self.uut showOverlay:@{} completion:^{}];
213
+	[self.overlayManager verify];
214
+}
215
+
216
+- (void)testShowOverlay_withCreatedLayout {
217
+	[self.store setReadyToReceiveCommands:true];
218
+	UIViewController* layoutVC = [RNNRootViewController new];
219
+	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]).andReturn(layoutVC);
220
+	
221
+	[[self.overlayManager expect] showOverlay:layoutVC];
222
+	[self.uut showOverlay:@{} completion:^{}];
223
+	[self.overlayManager verify];
224
+}
225
+
226
+- (void)testShowOverlay_invokeNavigationCommandEventWithLayout {
227
+	[self.store setReadyToReceiveCommands:true];
228
+	OCMStub([self.overlayManager showOverlay:[OCMArg any]]);
229
+	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]);
230
+
231
+	NSDictionary* layout = @{};
232
+	
233
+	[[self.eventEmmiter expect] sendOnNavigationCommandCompletion:@"showOverlay" params:[OCMArg any]];
234
+	[self.uut showOverlay:layout completion:^{}];
235
+	[self.eventEmmiter verify];
236
+}
237
+
238
+- (void)testDismissOverlay_findComponentFromOverlayStore {
239
+	[self.store setReadyToReceiveCommands:true];
240
+	NSString* componentId = @"componentId";
241
+	[[self.overlayStore expect] findComponentForId:componentId];
242
+	[self.uut dismissOverlay:componentId completion:^{} rejection:^(NSString *code, NSString *message, NSError *error) {}];
243
+	[self.overlayStore verify];
244
+}
245
+
246
+- (void)testDismissOverlay_dismissReturnedViewController {
247
+	[self.store setReadyToReceiveCommands:true];
248
+	NSString* componentId = @"componentId";
249
+	UIViewController* returnedView = [UIViewController new];
250
+	OCMStub([self.overlayStore findComponentForId:componentId]).andReturn(returnedView);
251
+	
252
+	[[self.overlayManager expect] dismissOverlay:returnedView];
253
+	[self.uut dismissOverlay:componentId completion:^{} rejection:^(NSString *code, NSString *message, NSError *error) {}];
254
+	[self.overlayManager verify];
255
+}
256
+
257
+- (void)testDismissOverlay_handleErrorIfNoOverlayExists {
258
+	[self.store setReadyToReceiveCommands:true];
259
+	NSString* componentId = @"componentId";
260
+    id errorHandlerMockClass = [OCMockObject mockForClass:[RNNErrorHandler class]];
261
+	
262
+    [[errorHandlerMockClass expect] reject:[OCMArg any] withErrorCode:1010 errorDescription:[OCMArg any]];
263
+    [self.uut dismissOverlay:componentId completion:[OCMArg any] rejection:[OCMArg any]];
264
+    [errorHandlerMockClass verify];
265
+}
266
+
267
+- (void)testDismissOverlay_invokeNavigationCommandEvent {
268
+	[self.store setReadyToReceiveCommands:true];
269
+	NSString* componentId = @"componentId";
270
+	OCMStub([self.overlayStore findComponentForId:componentId]).andReturn([UIViewController new]);
271
+	
272
+	[[self.eventEmmiter expect] sendOnNavigationCommandCompletion:@"dismissOverlay" params:[OCMArg any]];
273
+	[self.uut dismissOverlay:componentId completion:^{
274
+		
275
+	} rejection:^(NSString *code, NSString *message, NSError *error) {}];
276
+	 
277
+	[self.eventEmmiter verify];
278
+}
279
+
187 280
 @end

+ 101
- 102
lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m View File

@@ -22,7 +22,7 @@
22 22
 	[super setUp];
23 23
 	self.creator = nil;
24 24
 	self.store = [RNNStore new];
25
-	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator store:self.store eventEmitter:nil andBridge:nil];
25
+	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator eventEmitter:nil andBridge:nil];
26 26
 }
27 27
 
28 28
 - (void)tearDown {
@@ -30,16 +30,15 @@
30 30
 }
31 31
 
32 32
 - (void)testCreateLayout_EmptyLayout {
33
-	XCTAssertThrows([self.factory createLayoutAndSaveToStore:@{}]);
33
+	XCTAssertThrows([self.factory createLayout:@{} saveToStore:self.store]);
34 34
 }
35 35
 
36 36
 - (void)testCreateLayout_ComponentLayout {
37
-	
38
-	id ans = [self.factory createLayoutAndSaveToStore:
39
-			  @{@"id": @"cntId",
40
-				@"type": @"Component",
41
-				@"data": @{},
42
-				@"children": @[]}];
37
+	NSDictionary* layout = @{@"id": @"cntId",
38
+							 @"type": @"Component",
39
+							 @"data": @{},
40
+							 @"children": @[]};
41
+	id ans = [self.factory createLayout:layout saveToStore:self.store];
43 42
 	XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]);
44 43
 }
45 44
 
@@ -48,50 +47,50 @@
48 47
 		return [UIViewController new];
49 48
 	}];
50 49
 	
51
-	id ans = [self.factory createLayoutAndSaveToStore:
52
-			  @{@"id": @"cntId",
53
-				@"type": @"ExternalComponent",
54
-				@"data": @{@"name": @"externalComponent"},
55
-				@"children": @[]}];
50
+	NSDictionary* layout =  @{@"id": @"cntId",
51
+							  @"type": @"ExternalComponent",
52
+							  @"data": @{@"name": @"externalComponent"},
53
+							  @"children": @[]};
54
+	id ans = [self.factory createLayout:layout saveToStore:self.store];
56 55
 	XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]);
57 56
 }
58 57
 
59 58
 - (void)testCreateLayout_ComponentStackLayout {
60
-	id ans = [self.factory createLayoutAndSaveToStore:
61
-			  @{@"id": @"cntId",
62
-				@"type": @"Stack",
63
-				@"data": @{},
64
-				@"children": @[]}];
59
+	NSDictionary* layout = @{@"id": @"cntId",
60
+							 @"type": @"Stack",
61
+							 @"data": @{},
62
+							 @"children": @[]};
63
+	id ans = [self.factory createLayout:layout saveToStore:self.store];
65 64
 	XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]);
66 65
 }
67 66
 
68 67
 - (void)testCreateLayout_SplitViewLayout {
69
-	id ans = [self.factory createLayoutAndSaveToStore:
70
-			  @{@"id": @"cntId",
71
-				@"type": @"SplitView",
72
-				@"data": @{},
73
-				@"children": @[
74
-						@{@"id": @"cntId_2",
75
-						  @"type": @"Component",
76
-						  @"data": @{},
77
-						  @"children": @[]},
78
-			  @{@"id": @"cntId_3",
79
-				@"type": @"Component",
80
-				@"data": @{},
81
-				@"children": @[]}]}];
68
+	NSDictionary* layout = @{@"id": @"cntId",
69
+							 @"type": @"SplitView",
70
+							 @"data": @{},
71
+							 @"children": @[
72
+									 @{@"id": @"cntId_2",
73
+									   @"type": @"Component",
74
+									   @"data": @{},
75
+									   @"children": @[]},
76
+									 @{@"id": @"cntId_3",
77
+									   @"type": @"Component",
78
+									   @"data": @{},
79
+									   @"children": @[]}]};
80
+	id ans = [self.factory createLayout:layout saveToStore:self.store];
82 81
 	XCTAssertTrue([ans isMemberOfClass:[RNNSplitViewController class]]);
83 82
 }
84 83
 
85 84
 - (void)testCreateLayout_ComponentStackLayoutRecursive {
86
-	RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayoutAndSaveToStore:
87
-															 @{@"id": @"cntId",
88
-															   @"type": @"Stack",
89
-															   @"data": @{},
90
-															   @"children": @[
91
-																	   @{@"id": @"cntId_2",
92
-																		 @"type": @"Component",
93
-																		 @"data": @{},
94
-																		 @"children": @[]}]}];
85
+	NSDictionary* layout = @{@"id": @"cntId",
86
+							 @"type": @"Stack",
87
+							 @"data": @{},
88
+							 @"children": @[
89
+									 @{@"id": @"cntId_2",
90
+									   @"type": @"Component",
91
+									   @"data": @{},
92
+									   @"children": @[]}]};
93
+	RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayout:layout saveToStore:self.store];
95 94
 	
96 95
 	XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]);
97 96
 	XCTAssertTrue(ans.childViewControllers.count == 1);
@@ -99,20 +98,20 @@
99 98
 }
100 99
 
101 100
 - (void)testCreateLayout_BottomTabsLayout {
102
-	RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayoutAndSaveToStore:
103
-														@{
104
-														  @"id": @"cntId",
105
-														  @"type": @"BottomTabs",
106
-														  @"data": @{},
107
-														  @"children": @[
108
-																  @{@"id": @"cntId_2",
109
-																	@"type": @"Stack",
110
-																	@"data": @{},
111
-																	@"children": @[
112
-																			@{@"id": @"cntId_3",
113
-																			  @"type": @"Component",
114
-																			  @"data": @{},
115
-																			  @"children": @[]}]}]}];
101
+	NSDictionary* layout = @{
102
+							 @"id": @"cntId",
103
+							 @"type": @"BottomTabs",
104
+							 @"data": @{},
105
+							 @"children": @[
106
+									 @{@"id": @"cntId_2",
107
+									   @"type": @"Stack",
108
+									   @"data": @{},
109
+									   @"children": @[
110
+											   @{@"id": @"cntId_3",
111
+												 @"type": @"Component",
112
+												 @"data": @{},
113
+												 @"children": @[]}]}]};
114
+	RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayout:layout saveToStore:self.store];
116 115
 	
117 116
 	XCTAssertTrue([tabBar isMemberOfClass:[RNNTabBarController class]]);
118 117
 	XCTAssertTrue(tabBar.childViewControllers.count == 1);
@@ -124,54 +123,54 @@
124 123
 }
125 124
 
126 125
 - (void)testCreateLayout_TopTabsLayout {
127
-	RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayoutAndSaveToStore:
128
-														  @{
129
-															@"id": @"cntId",
130
-															@"type": @"TopTabs",
131
-															@"data": @{},
132
-															@"children": @[
133
-																	@{@"id": @"cntId_2",
134
-																	  @"type": @"Stack",
135
-																	  @"data": @{},
136
-																	  @"children": @[
137
-																			  @{@"id": @"cntId_3",
138
-																				@"type": @"Component",
139
-																				@"data": @{},
140
-																				@"children": @[]}]}]}];
126
+	NSDictionary* layout = @{
127
+							 @"id": @"cntId",
128
+							 @"type": @"TopTabs",
129
+							 @"data": @{},
130
+							 @"children": @[
131
+									 @{@"id": @"cntId_2",
132
+									   @"type": @"Stack",
133
+									   @"data": @{},
134
+									   @"children": @[
135
+											   @{@"id": @"cntId_3",
136
+												 @"type": @"Component",
137
+												 @"data": @{},
138
+												 @"children": @[]}]}]};
139
+	RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayout:layout saveToStore:self.store];
141 140
 	
142 141
 	XCTAssertTrue([tabBar isMemberOfClass:[RNNTopTabsViewController class]]);
143 142
 }
144 143
 
145 144
 - (void)testCreateLayout_ComponentSideMenuLayoutCenterLeftRight {
146
-	RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayoutAndSaveToStore:
147
-														   @{@"id": @"cntId",
148
-															 @"type": @"SideMenuRoot",
149
-															 @"data": @{},
150
-															 @"children": @[
151
-																	 @{@"id": @"cntI_2",
152
-																	   @"type": @"SideMenuCenter",
153
-																	   @"data": @{},
154
-																	   @"children": @[
155
-																			   @{@"id": @"cntId_3",
156
-																				 @"type": @"Component",
157
-																				 @"data": @{},
158
-																				 @"children": @[]}]},
159
-																	 @{@"id": @"cntI_4",
160
-																	   @"type": @"SideMenuLeft",
161
-																	   @"data": @{},
162
-																	   @"children": @[
163
-																			   @{@"id": @"cntId_5",
164
-																				 @"type": @"Component",
165
-																				 @"data": @{},
166
-																				 @"children": @[]}]},
167
-																	 @{@"id": @"cntI_6",
168
-																	   @"type": @"SideMenuRight",
169
-																	   @"data": @{},
170
-																	   @"children": @[
171
-																			   @{@"id": @"cntId_7",
172
-																				 @"type": @"Component",
173
-																				 @"data": @{},
174
-																				 @"children": @[]}]}]}];
145
+	NSDictionary* layout = @{@"id": @"cntId",
146
+							 @"type": @"SideMenuRoot",
147
+							 @"data": @{},
148
+							 @"children": @[
149
+									 @{@"id": @"cntI_2",
150
+									   @"type": @"SideMenuCenter",
151
+									   @"data": @{},
152
+									   @"children": @[
153
+											   @{@"id": @"cntId_3",
154
+												 @"type": @"Component",
155
+												 @"data": @{},
156
+												 @"children": @[]}]},
157
+									 @{@"id": @"cntI_4",
158
+									   @"type": @"SideMenuLeft",
159
+									   @"data": @{},
160
+									   @"children": @[
161
+											   @{@"id": @"cntId_5",
162
+												 @"type": @"Component",
163
+												 @"data": @{},
164
+												 @"children": @[]}]},
165
+									 @{@"id": @"cntI_6",
166
+									   @"type": @"SideMenuRight",
167
+									   @"data": @{},
168
+									   @"children": @[
169
+											   @{@"id": @"cntId_7",
170
+												 @"type": @"Component",
171
+												 @"data": @{},
172
+												 @"children": @[]}]}]};
173
+	RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayout:layout saveToStore:self.store];
175 174
 	XCTAssertTrue([ans isMemberOfClass:[RNNSideMenuController class]]);
176 175
 	XCTAssertTrue([ans isKindOfClass:[UIViewController class]]);
177 176
 	XCTAssertTrue([ans.center isMemberOfClass:[RNNSideMenuChildVC class]]);
@@ -190,11 +189,11 @@
190 189
 
191 190
 - (void)testCreateLayout_addComponentToStore {
192 191
 	NSString *componentId = @"cntId";
193
-	UIViewController *ans = [self.factory createLayoutAndSaveToStore:
194
-							 @{@"id": componentId,
195
-							   @"type": @"Component",
196
-							   @"data": @{},
197
-							   @"children": @[]}];
192
+	NSDictionary* layout = @{@"id": componentId,
193
+							 @"type": @"Component",
194
+							 @"data": @{},
195
+							 @"children": @[]};
196
+	UIViewController *ans = [self.factory createLayout:layout saveToStore:self.store];
198 197
 	
199 198
 	UIViewController *storeAns = [self.store findComponentForId:componentId];
200 199
 	XCTAssertEqualObjects(ans, storeAns);

+ 8
- 0
lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m View File

@@ -2,6 +2,7 @@
2 2
 #import <OCMock/OCMock.h>
3 3
 #import "RNNViewControllerPresenter.h"
4 4
 #import "UIViewController+RNNOptions.h"
5
+#import "RNNReactView.h"
5 6
 
6 7
 @interface RNNViewControllerPresenterTest : XCTestCase
7 8
 
@@ -59,6 +60,13 @@
59 60
 	[(id)self.bindedViewController verify];
60 61
 }
61 62
 
63
+- (void)testApplyOptions_setOverlayTouchOutsideIfHasValue {
64
+    self.options.overlay.interceptTouchOutside = [[Bool alloc] initWithBOOL:YES];
65
+    [[(id)self.bindedViewController expect] rnn_setInterceptTouchOutside:YES];
66
+    [self.uut applyOptions:self.options];
67
+    [(id)self.bindedViewController verify];
68
+}
69
+
62 70
 - (void)testApplyOptionsOnInit_shouldSetModalPresentetionStyleWithDefault {
63 71
 	[[(id)self.bindedViewController expect] rnn_setModalPresentationStyle:UIModalPresentationFullScreen];
64 72
 	[self.uut applyOptionsOnInit:self.options];

+ 4
- 4
lib/ios/ReactNativeNavigationTests/UIViewController+RNNOptionsTest.m View File

@@ -84,10 +84,10 @@
84 84
 }
85 85
 
86 86
 - (void)testSetBackgroundImageShouldAddUIImageViewSubviewWithImage {
87
-	UIImage* image = [UIImage new];
88
-	[self.uut rnn_setBackgroundImage:image];
89
-	UIImageView* imageView = [[[self.uut view] subviews] firstObject];
90
-	XCTAssertEqual(imageView.image, image);
87
+    UIImage* image = [UIImage new];
88
+    [self.uut rnn_setBackgroundImage:image];
89
+    UIImageView* imageView = [[[self.uut view] subviews] firstObject];
90
+    XCTAssertEqual(imageView.image, image);
91 91
 }
92 92
 
93 93
 @end

+ 2
- 0
lib/ios/UIViewController+RNNOptions.h View File

@@ -32,6 +32,8 @@
32 32
 
33 33
 - (void)rnn_setBackgroundColor:(UIColor *)backgroundColor;
34 34
 
35
+- (void)rnn_setInterceptTouchOutside:(BOOL)interceptTouchOutside;
36
+
35 37
 - (BOOL)isModal;
36 38
 
37 39
 @end

+ 8
- 0
lib/ios/UIViewController+RNNOptions.m View File

@@ -1,4 +1,6 @@
1 1
 #import "UIViewController+RNNOptions.h"
2
+#import <React/RCTRootView.h>
3
+
2 4
 #define kStatusBarAnimationDuration 0.35
3 5
 const NSInteger BLUR_STATUS_TAG = 78264801;
4 6
 
@@ -148,5 +150,11 @@ const NSInteger BLUR_STATUS_TAG = 78264801;
148 150
 	return NO;
149 151
 }
150 152
 
153
+- (void)rnn_setInterceptTouchOutside:(BOOL)interceptTouchOutside {
154
+	if ([self.view isKindOfClass:[RCTRootView class]]) {
155
+		RCTRootView* rootView = (RCTRootView*)self.view;
156
+		rootView.passThroughTouches = !interceptTouchOutside;
157
+	}
158
+}
151 159
 
152 160
 @end