Browse Source

Decoupled native stack behavior from RNNStore

yogevbd 6 years ago
parent
commit
51ffeee605

+ 52
- 29
lib/ios/RNNCommandsHandler.m View File

27
 @implementation RNNCommandsHandler {
27
 @implementation RNNCommandsHandler {
28
 	RNNControllerFactory *_controllerFactory;
28
 	RNNControllerFactory *_controllerFactory;
29
 	RNNStore *_store;
29
 	RNNStore *_store;
30
-	RNNNavigationStackManager* _navigationStackManager;
31
 	RNNModalManager* _modalManager;
30
 	RNNModalManager* _modalManager;
32
 	RNNOverlayManager* _overlayManager;
31
 	RNNOverlayManager* _overlayManager;
32
+	RNNNavigationStackManager* _stackManager;
33
 	RNNEventEmitter* _eventEmitter;
33
 	RNNEventEmitter* _eventEmitter;
34
 }
34
 }
35
 
35
 
38
 	_store = store;
38
 	_store = store;
39
 	_controllerFactory = controllerFactory;
39
 	_controllerFactory = controllerFactory;
40
 	_eventEmitter = eventEmitter;
40
 	_eventEmitter = eventEmitter;
41
-	_navigationStackManager = [[RNNNavigationStackManager alloc] initWithStore:_store];
42
 	_modalManager = [[RNNModalManager alloc] initWithStore:_store];
41
 	_modalManager = [[RNNModalManager alloc] initWithStore:_store];
42
+	_stackManager = [[RNNNavigationStackManager alloc] init];
43
 	_overlayManager = [[RNNOverlayManager alloc] initWithStore:_store];
43
 	_overlayManager = [[RNNOverlayManager alloc] initWithStore:_store];
44
 	return self;
44
 	return self;
45
 }
45
 }
98
 -(void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
98
 -(void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
99
 	[self assertReady];
99
 	[self assertReady];
100
 
100
 
101
-	UIViewController<RNNRootViewProtocol, UIViewControllerPreviewingDelegate> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
102
-
101
+	RNNRootViewController *newVc = (RNNRootViewController *)[_controllerFactory createLayoutAndSaveToStore:layout];
102
+	UIViewController *fromVC = [_store findComponentForId:componentId];
103
+	
103
 	if (newVc.options.preview.elementId) {
104
 	if (newVc.options.preview.elementId) {
104
 		UIViewController* vc = [_store findComponentForId:componentId];
105
 		UIViewController* vc = [_store findComponentForId:componentId];
105
 
106
 
127
 			[rootVc registerForPreviewingWithDelegate:(id)rootVc sourceView:elementView];
128
 			[rootVc registerForPreviewingWithDelegate:(id)rootVc sourceView:elementView];
128
 		}
129
 		}
129
 	} else {
130
 	} else {
130
-		[_navigationStackManager push:newVc onTop:componentId completion:^{
131
-			[_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}];
132
-			completion();
133
-		} rejection:rejection];
131
+		[newVc onReactViewReady:^{
132
+			id animationDelegate = (newVc.options.animations.push.hasCustomAnimation || newVc.isCustomTransitioned) ? newVc : nil;
133
+			[_stackManager push:newVc onTop:fromVC animated:newVc.options.animations.push.enable animationDelegate:animationDelegate completion:^{
134
+				[_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}];
135
+				completion();
136
+			} rejection:rejection];
137
+		}];
134
 	}
138
 	}
135
 }
139
 }
136
 
140
 
137
 -(void)setStackRoot:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
141
 -(void)setStackRoot:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
138
 	[self assertReady];
142
 	[self assertReady];
139
 	
143
 	
140
-	UIViewController<RNNRootViewProtocol> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
144
+	UIViewController<RNNRootViewProtocol> *newVC = [_controllerFactory createLayoutAndSaveToStore:layout];
145
+	UIViewController *fromVC = [_store findComponentForId:componentId];
141
 	__weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter;
146
 	__weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter;
142
-	[_navigationStackManager setStackRoot:newVc fromComponent:componentId completion:^{
147
+	[_stackManager setStackRoot:newVC fromViewController:fromVC animated:newVC.options.animations.push.enable completion:^{
143
 		[weakEventEmitter sendOnNavigationCommandCompletion:setStackRoot params:@{@"componentId": componentId}];
148
 		[weakEventEmitter sendOnNavigationCommandCompletion:setStackRoot params:@{@"componentId": componentId}];
144
 		completion();
149
 		completion();
145
 	} rejection:rejection];
150
 	} rejection:rejection];
148
 -(void)pop:(NSString*)componentId options:(NSDictionary*)options completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
153
 -(void)pop:(NSString*)componentId options:(NSDictionary*)options completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
149
 	[self assertReady];
154
 	[self assertReady];
150
 
155
 
151
-	[CATransaction begin];
152
-	[CATransaction setCompletionBlock:^{
156
+	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
157
+	UINavigationController *nvc = vc.navigationController;
158
+
159
+	if ([nvc topViewController] == vc) {
160
+		if (vc.options.animations.pop) {
161
+			nvc.delegate = vc;
162
+		} else {
163
+			nvc.delegate = nil;
164
+		}
165
+	} else {
166
+		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;
167
+		[vcs removeObject:vc];
168
+		[nvc setViewControllers:vcs animated:vc.options.animations.pop.enable];
169
+	}
170
+	
171
+	[_stackManager pop:vc animated:vc.options.animations.pop.enable completion:^{
172
+		[_store removeComponent:componentId];
153
 		[_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}];
173
 		[_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}];
154
 		completion();
174
 		completion();
175
+	} rejection:^(NSString *code, NSString *message, NSError *error) {
176
+		
155
 	}];
177
 	}];
156
-	
157
-	NSDictionary* animationData = options[@"customTransition"];
158
-	RNNAnimationOptions* transitionOptions = [[RNNAnimationOptions alloc] initWithDict:animationData];
159
-	
160
-	if (transitionOptions.animations){
161
-		[_navigationStackManager pop:componentId withTransitionOptions:transitionOptions rejection:rejection];
162
-	} else {
163
-		[_navigationStackManager pop:componentId withTransitionOptions:nil rejection:rejection];
164
-	}
165
-	[CATransaction commit];
166
 }
178
 }
167
 
179
 
168
 -(void) popTo:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
180
 -(void) popTo:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
169
 	[self assertReady];
181
 	[self assertReady];
170
-	[CATransaction begin];
171
-	[CATransaction setCompletionBlock:^{
182
+	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
183
+
184
+	[_stackManager popTo:vc animated:vc.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
172
 		[_eventEmitter sendOnNavigationCommandCompletion:popTo params:@{@"componentId": componentId}];
185
 		[_eventEmitter sendOnNavigationCommandCompletion:popTo params:@{@"componentId": componentId}];
186
+		[self removePopedViewControllers:poppedViewControllers];
173
 		completion();
187
 		completion();
188
+	} rejection:^(NSString *code, NSString *message, NSError *error) {
189
+		
174
 	}];
190
 	}];
175
-	
176
-	[_navigationStackManager popTo:componentId rejection:rejection];
177
-	
178
-	[CATransaction commit];
179
 }
191
 }
180
 
192
 
181
 -(void) popToRoot:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
193
 -(void) popToRoot:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
182
 	[self assertReady];
194
 	[self assertReady];
195
+	RNNRootViewController *newVc = (RNNRootViewController*)[_store findComponentForId:componentId];
183
 
196
 
184
 	[CATransaction begin];
197
 	[CATransaction begin];
185
 	[CATransaction setCompletionBlock:^{
198
 	[CATransaction setCompletionBlock:^{
187
 		completion();
200
 		completion();
188
 	}];
201
 	}];
189
 	
202
 	
190
-	[_navigationStackManager popToRoot:componentId rejection:rejection];
203
+	[_stackManager popToRoot:newVc animated:newVc.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
204
+		[self removePopedViewControllers:poppedViewControllers];
205
+	} rejection:^(NSString *code, NSString *message, NSError *error) {
206
+		
207
+	}];
191
 	
208
 	
192
 	[CATransaction commit];
209
 	[CATransaction commit];
193
 }
210
 }
251
 
268
 
252
 #pragma mark - private
269
 #pragma mark - private
253
 
270
 
271
+-(void)removePopedViewControllers:(NSArray*)viewControllers {
272
+	for (UIViewController *popedVC in viewControllers) {
273
+		[_store removeComponentByViewControllerInstance:popedVC];
274
+	}
275
+}
276
+
254
 -(void) assertReady {
277
 -(void) assertReady {
255
 	if (!_store.isReadyToReceiveCommands) {
278
 	if (!_store.isReadyToReceiveCommands) {
256
 		[[NSException exceptionWithName:@"BridgeNotLoadedError"
279
 		[[NSException exceptionWithName:@"BridgeNotLoadedError"

+ 2
- 2
lib/ios/RNNNavigationController.m View File

12
 	return ((UIViewController<RNNRootViewProtocol>*)self.topViewController);
12
 	return ((UIViewController<RNNRootViewProtocol>*)self.topViewController);
13
 }
13
 }
14
 
14
 
15
-- (BOOL)isCustomTransitioned {
16
-	return NO;
15
+- (UINavigationController *)navigationController {
16
+	return self;
17
 }
17
 }
18
 
18
 
19
 - (BOOL)isCustomViewController {
19
 - (BOOL)isCustomViewController {

+ 11
- 8
lib/ios/RNNNavigationStackManager.h View File

1
 #import <Foundation/Foundation.h>
1
 #import <Foundation/Foundation.h>
2
 #import <UIKit/UIKit.h>
2
 #import <UIKit/UIKit.h>
3
-#import "RNNStore.h"
3
+
4
+typedef void (^RNNTransitionCompletionBlock)(void);
5
+typedef void (^RNNPopCompletionBlock)(NSArray* poppedViewControllers);
6
+typedef void (^RNNTransitionRejectionBlock)(NSString *code, NSString *message, NSError *error);
4
 
7
 
5
 @interface RNNNavigationStackManager : NSObject
8
 @interface RNNNavigationStackManager : NSObject
6
 
9
 
7
-@property (nonatomic) int loadCount;
8
--(instancetype)initWithStore:(RNNStore*)store;
10
+- (void)push:(UIViewController *)newTop onTop:(UIViewController *)onTopViewController animated:(BOOL)animated animationDelegate:(id)animationDelegate completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection;
11
+
12
+- (void)pop:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection;
13
+
14
+- (void)popTo:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNPopCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection;
9
 
15
 
16
+- (void)popToRoot:(UIViewController*)viewController animated:(BOOL)animated completion:(RNNPopCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection;
10
 
17
 
11
--(void)push:(UIViewController<RNNRootViewProtocol>*)newTop onTop:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection;
12
--(void)pop:(NSString*)componentId withTransitionOptions:(RNNAnimationOptions*)transitionOptions rejection:(RCTPromiseRejectBlock)rejection;
13
--(void)popTo:(NSString*)componentId rejection:(RCTPromiseRejectBlock)rejection;
14
--(void)popToRoot:(NSString*)componentId rejection:(RCTPromiseRejectBlock)rejection;
15
--(void)setStackRoot:(UIViewController<RNNRootViewProtocol> *)newRoot fromComponent:(NSString *)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection;
18
+- (void)setStackRoot:(UIViewController *)newRoot fromViewController:(UIViewController *)fromViewController animated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection;
16
 
19
 
17
 @end
20
 @end

+ 36
- 115
lib/ios/RNNNavigationStackManager.m View File

1
 #import "RNNNavigationStackManager.h"
1
 #import "RNNNavigationStackManager.h"
2
-#import "RNNRootViewController.h"
3
-#import "RNNAnimator.h"
4
 #import "RNNErrorHandler.h"
2
 #import "RNNErrorHandler.h"
5
-#import "RNNNavigationController.h"
6
 
3
 
7
-dispatch_queue_t RCTGetUIManagerQueue(void);
8
-@interface RNNNavigationStackManager()
9
-@property (nonatomic, strong) RNNNavigationController* nvc;
10
-@property (nonatomic, strong) RNNRootViewController* toVC;
11
-@end
4
+typedef void (^RNNAnimationBlock)(void);
12
 
5
 
13
-@implementation RNNNavigationStackManager {
14
-	RNNStore *_store;
15
-	RNNTransitionCompletionBlock _completionBlock;
16
-}
6
+@implementation RNNNavigationStackManager
17
 
7
 
18
--(instancetype)initWithStore:(RNNStore*)store {
19
-	self = [super init];
20
-	_store = store;
21
-	return self;
22
-}
8
+- (void)push:(UIViewController *)newTop onTop:(UIViewController *)onTopViewController animated:(BOOL)animated animationDelegate:(id)animationDelegate completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
9
+	UINavigationController *nvc = onTopViewController.navigationController;
23
 
10
 
24
--(void)push:(UIViewController<RNNRootViewProtocol> *)newTop onTop:(NSString *)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
25
-	RNNNavigationController *nvc = [self getComponentStack:componentId];
26
-	[self assertNavigationControllerExist:nvc reject:rejection];
27
-	[self preparePush:newTop onTopVC:nvc completion:completion];
28
-	if ([newTop isCustomViewController]) {
29
-		[self pushAfterLoad:nil];
30
-	} else {
31
-		[self waitForContentToAppearAndThen:@selector(pushAfterLoad:)];
32
-	}
33
-}
34
-
35
--(void)preparePush:(UIViewController<RNNRootViewProtocol> *)newTop onTopVC:(RNNNavigationController*)nvc completion:(RNNTransitionCompletionBlock)completion {
36
-	self.toVC = (RNNRootViewController*)newTop;
37
-	self.nvc = nvc;
38
-	
39
-	
40
-	if (self.toVC.options.animations.push.hasCustomAnimation || self.toVC.isCustomTransitioned) {
41
-		nvc.delegate = newTop;
11
+	if (animationDelegate) {
12
+		nvc.delegate = animationDelegate;
42
 	} else {
13
 	} else {
43
 		nvc.delegate = nil;
14
 		nvc.delegate = nil;
44
 		nvc.interactivePopGestureRecognizer.delegate = nil;
15
 		nvc.interactivePopGestureRecognizer.delegate = nil;
45
 	}
16
 	}
46
 	
17
 	
47
-	_completionBlock = completion;
18
+	[self performAnimationBlock:^{
19
+		[nvc pushViewController:newTop animated:animated];
20
+	} completion:completion];
48
 }
21
 }
49
 
22
 
50
--(void)waitForContentToAppearAndThen:(SEL)nameOfSelector {
51
-	[[NSNotificationCenter defaultCenter] addObserver:self
52
-											 selector:nameOfSelector
53
-												 name: @"RCTContentDidAppearNotification"
54
-											   object:nil];
23
+- (void)pop:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection {
24
+	[self performAnimationBlock:^{
25
+		[viewController.navigationController popViewControllerAnimated:animated];
26
+	} completion:completion];
55
 }
27
 }
56
 
28
 
57
--(void)pushAfterLoad:(NSDictionary*)notif {
58
-	[[NSNotificationCenter defaultCenter] removeObserver:self name:@"RCTContentDidAppearNotification" object:nil];
59
-	[CATransaction begin];
60
-	[CATransaction setCompletionBlock:^{
61
-		if (_completionBlock) {
62
-			_completionBlock();
63
-			_completionBlock = nil;
29
+- (void)popTo:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNPopCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection; {
30
+	__block NSArray* poppedVCs;
31
+	
32
+	[self performAnimationBlock:^{
33
+		poppedVCs = [viewController.navigationController popToViewController:viewController animated:animated];
34
+	} completion:^{
35
+		if (completion) {
36
+			completion(poppedVCs);
64
 		}
37
 		}
65
 	}];
38
 	}];
66
-	
67
-	[self.nvc pushViewController:self.toVC animated:self.toVC.options.animations.push.enable];
68
-	[CATransaction commit];
69
-	
70
-	self.toVC = nil;
71
-	self.nvc.interactivePopGestureRecognizer.delegate = nil;
72
-	self.nvc = nil;
73
 }
39
 }
74
 
40
 
75
--(void)pop:(NSString *)componentId withTransitionOptions:(RNNAnimationOptions *)transitionOptions rejection:(RCTPromiseRejectBlock)rejection {
76
-	RNNRootViewController* vc = (RNNRootViewController*)[_store findComponentForId:componentId];
77
-	UINavigationController* nvc = [self getComponentStack:componentId];
78
-	[self assertNavigationControllerExist:nvc reject:rejection];
41
+- (void)popToRoot:(UIViewController*)viewController animated:(BOOL)animated completion:(RNNPopCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection {
42
+	__block NSArray* poppedVCs;
79
 	
43
 	
80
-	if ([nvc topViewController] == vc) {
81
-		if (vc.options.animations.pop) {
82
-			nvc.delegate = vc;
83
-			[nvc popViewControllerAnimated:vc.options.animations.pop.enable];
84
-		} else {
85
-			nvc.delegate = nil;
86
-			[nvc popViewControllerAnimated:vc.options.animations.pop.enable];
87
-		}
88
-	} else {
89
-		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;
90
-		[vcs removeObject:vc];
91
-		[nvc setViewControllers:vcs animated:vc.options.animations.pop.enable];
92
-	}
93
-	[_store removeComponent:componentId];
44
+	[self performAnimationBlock:^{
45
+		poppedVCs = [viewController.navigationController popToRootViewControllerAnimated:animated];
46
+	} completion:^{
47
+		completion(poppedVCs);
48
+	}];
94
 }
49
 }
95
 
50
 
96
--(void)popTo:(NSString*)componentId rejection:(RCTPromiseRejectBlock)rejection {
97
-	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
98
-	RNNNavigationController *nvc = [self getComponentStack:componentId];
99
-	[self assertNavigationControllerExist:nvc reject:rejection];
51
+- (void)setStackRoot:(UIViewController *)newRoot fromViewController:(UIViewController *)fromViewController animated:(BOOL)animated completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection {
52
+	UINavigationController* nvc = fromViewController.navigationController;
100
 	
53
 	
101
-	if (vc && nvc) {
102
-		NSArray *poppedVCs = [nvc popToViewController:vc animated:vc.options.animations.pop.enable];
103
-		[self removePopedViewControllers:poppedVCs];
104
-	}
54
+	[self performAnimationBlock:^{
55
+		[nvc setViewControllers:@[newRoot] animated:animated];
56
+	} completion:completion];
105
 }
57
 }
106
 
58
 
107
--(void)popToRoot:(NSString*)componentId rejection:(RCTPromiseRejectBlock)rejection {
108
-	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
109
-	RNNNavigationController *nvc = [self getComponentStack:componentId];
110
-	[self assertNavigationControllerExist:nvc reject:rejection];
111
-	NSArray* poppedVCs = [nvc popToRootViewControllerAnimated:vc.options.animations.pop.enable];
112
-	[self removePopedViewControllers:poppedVCs];
113
-}
114
-
115
--(void)setStackRoot:(UIViewController<RNNRootViewProtocol> *)newRoot fromComponent:(NSString *)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
116
-	RNNNavigationController* nvc = [self getComponentStack:componentId];
59
+# pragma mark Private
117
 
60
 
118
-	[self assertNavigationControllerExist:nvc reject:rejection];
119
-	
61
+- (void)performAnimationBlock:(RNNAnimationBlock)animationBlock completion:(RNNTransitionCompletionBlock)completion {
120
 	[CATransaction begin];
62
 	[CATransaction begin];
121
 	[CATransaction setCompletionBlock:^{
63
 	[CATransaction setCompletionBlock:^{
122
 		if (completion) {
64
 		if (completion) {
124
 		}
66
 		}
125
 	}];
67
 	}];
126
 	
68
 	
127
-	[nvc setViewControllers:@[newRoot] animated:newRoot.options.animations.push.enable];
69
+	animationBlock();
128
 	
70
 	
129
 	[CATransaction commit];
71
 	[CATransaction commit];
130
 }
72
 }
131
 
73
 
132
-- (void)assertNavigationControllerExist:(UINavigationController *)viewController reject:(RCTPromiseRejectBlock)reject {
133
-	if (![viewController isKindOfClass:[UINavigationController class]]) {
134
-		_completionBlock = nil;
135
-		[RNNErrorHandler reject:reject withErrorCode:RNNCommandErrorCodeNoStack errorDescription:[NSString stringWithFormat:@"%@ should be called from a stack child component", [RNNErrorHandler getCallerFunctionName]]];
136
-	}
137
-}
138
-
139
-- (RNNNavigationController*)getComponentStack:(NSString *)componentId {
140
-	UIViewController* component = [_store findComponentForId:componentId];
141
-	if ([component isKindOfClass:[UINavigationController class]]) {
142
-		return (RNNNavigationController*)component;
143
-	} else {
144
-		return (RNNNavigationController*)component.navigationController;
145
-	}
146
-}
147
-
148
--(void)removePopedViewControllers:(NSArray*)viewControllers {
149
-	for (UIViewController *popedVC in viewControllers) {
150
-		[_store removeComponentByViewControllerInstance:popedVC];
151
-	}
152
-}
153
 
74
 
154
 @end
75
 @end

+ 5
- 2
lib/ios/RNNRootViewController.h View File

9
 #import "RNNTopTabsViewController.h"
9
 #import "RNNTopTabsViewController.h"
10
 #import "RNNRootViewProtocol.h"
10
 #import "RNNRootViewProtocol.h"
11
 
11
 
12
+@class RNNRootViewController;
13
+
14
+typedef void (^RNNReactViewReadyCompletionBlock)(void);
15
+
12
 @interface RNNRootViewController : UIViewController	<RNNRootViewProtocol, UIViewControllerPreviewingDelegate, UISearchResultsUpdating>
16
 @interface RNNRootViewController : UIViewController	<RNNRootViewProtocol, UIViewControllerPreviewingDelegate, UISearchResultsUpdating>
13
 
17
 
14
 @property (nonatomic, strong) RNNNavigationOptions* options;
18
 @property (nonatomic, strong) RNNNavigationOptions* options;
27
 			   eventEmitter:(RNNEventEmitter*)eventEmitter
31
 			   eventEmitter:(RNNEventEmitter*)eventEmitter
28
 		  isExternalComponent:(BOOL)isExternalComponent;
32
 		  isExternalComponent:(BOOL)isExternalComponent;
29
 
33
 
34
+- (void)onReactViewReady:(RNNReactViewReadyCompletionBlock)readyBlock;
30
 
35
 
31
 -(void)applyTabBarItem;
36
 -(void)applyTabBarItem;
32
 -(void)applyTopTabsOptions;
37
 -(void)applyTopTabsOptions;
33
 
38
 
34
--(BOOL)isCustomTransitioned;
35
-
36
 @end
39
 @end

+ 24
- 0
lib/ios/RNNRootViewController.m View File

10
 	UIView* _customTopBar;
10
 	UIView* _customTopBar;
11
 	UIView* _customTopBarBackground;
11
 	UIView* _customTopBarBackground;
12
 }
12
 }
13
+
13
 @property (nonatomic, strong) NSString* componentName;
14
 @property (nonatomic, strong) NSString* componentName;
14
 @property (nonatomic) BOOL _statusBarHidden;
15
 @property (nonatomic) BOOL _statusBarHidden;
15
 @property (nonatomic) BOOL isExternalComponent;
16
 @property (nonatomic) BOOL isExternalComponent;
16
 @property (nonatomic) BOOL _optionsApplied;
17
 @property (nonatomic) BOOL _optionsApplied;
17
 @property (nonatomic, copy) void (^rotationBlock)(void);
18
 @property (nonatomic, copy) void (^rotationBlock)(void);
19
+@property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock;
20
+
18
 @end
21
 @end
19
 
22
 
20
 @implementation RNNRootViewController
23
 @implementation RNNRootViewController
36
 	
39
 	
37
 	if (!self.isExternalComponent) {
40
 	if (!self.isExternalComponent) {
38
 		self.view = [creator createRootView:self.componentName rootViewId:self.componentId];
41
 		self.view = [creator createRootView:self.componentName rootViewId:self.componentId];
42
+		[[NSNotificationCenter defaultCenter] addObserver:self
43
+												 selector:@selector(reactViewReady)
44
+													 name: @"RCTContentDidAppearNotification"
45
+												   object:nil];
39
 	}
46
 	}
40
 	
47
 	
41
 	[[NSNotificationCenter defaultCenter] addObserver:self
48
 	[[NSNotificationCenter defaultCenter] addObserver:self
75
 	[self.eventEmitter sendComponentDidDisappear:self.componentId componentName:self.componentName];
82
 	[self.eventEmitter sendComponentDidDisappear:self.componentId componentName:self.componentName];
76
 }
83
 }
77
 
84
 
85
+- (void)reactViewReady {
86
+	if (_reactViewReadyBlock) {
87
+		_reactViewReadyBlock();
88
+		_reactViewReadyBlock = nil;
89
+	}
90
+	
91
+	[[NSNotificationCenter defaultCenter] removeObserver:self name:@"RCTContentDidAppearNotification" object:nil];
92
+}
93
+
94
+- (void)onReactViewReady:(RNNReactViewReadyCompletionBlock)readyBlock {
95
+	if (self.isCustomViewController) {
96
+		readyBlock();
97
+	} else {
98
+		self.reactViewReadyBlock = readyBlock;
99
+	}
100
+}
101
+
78
 -(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
102
 -(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
79
 	[self.eventEmitter sendOnSearchBarUpdated:self.componentId
103
 	[self.eventEmitter sendOnSearchBarUpdated:self.componentId
80
 										 text:searchController.searchBar.text
104
 										 text:searchController.searchBar.text

+ 2
- 2
lib/ios/RNNRootViewProtocol.h View File

8
 - (void)performOnRotation:(void (^)(void))block;
8
 - (void)performOnRotation:(void (^)(void))block;
9
 - (void)optionsUpdated;
9
 - (void)optionsUpdated;
10
 - (void)applyModalOptions;
10
 - (void)applyModalOptions;
11
-
12
-@required
13
 - (BOOL)isCustomTransitioned;
11
 - (BOOL)isCustomTransitioned;
14
 - (RNNNavigationOptions*)options;
12
 - (RNNNavigationOptions*)options;
13
+
14
+@required
15
 - (NSString *)componentId;
15
 - (NSString *)componentId;
16
 
16
 
17
 @end
17
 @end

+ 0
- 8
lib/ios/RNNSideMenuChildVC.m View File

35
 	return self.child.preferredStatusBarStyle;
35
 	return self.child.preferredStatusBarStyle;
36
 }
36
 }
37
 
37
 
38
-- (BOOL)isCustomTransitioned {
39
-	return NO;
40
-}
41
-
42
-- (RNNOptions *)options {
43
-	return nil;
44
-}
45
-
46
 - (NSString *)componentId {
38
 - (NSString *)componentId {
47
 	return _child.componentId;
39
 	return _child.componentId;
48
 }
40
 }

+ 0
- 8
lib/ios/RNNSideMenuController.m View File

89
 	}
89
 	}
90
 }
90
 }
91
 
91
 
92
-- (BOOL)isCustomTransitioned {
93
-	return NO;
94
-}
95
-
96
-- (RNNOptions *)options {
97
-	return nil;
98
-}
99
-
100
 - (NSString *)componentId {
92
 - (NSString *)componentId {
101
 	return _center.componentId;
93
 	return _center.componentId;
102
 }
94
 }

+ 0
- 2
lib/ios/RNNSplitViewController.h View File

22
 			rootViewCreator:(id<RNNRootViewCreator>)creator
22
 			rootViewCreator:(id<RNNRootViewCreator>)creator
23
 				  eventEmitter:(RNNEventEmitter*)eventEmitter;
23
 				  eventEmitter:(RNNEventEmitter*)eventEmitter;
24
 
24
 
25
--(BOOL)isCustomTransitioned;
26
-
27
 @end
25
 @end

+ 0
- 4
lib/ios/RNNSplitViewController.m View File

37
 	[self.options mergeIfEmptyWith:options];
37
 	[self.options mergeIfEmptyWith:options];
38
 }
38
 }
39
 
39
 
40
--(BOOL)isCustomTransitioned {
41
-	return false;
42
-}
43
-
44
 @end
40
 @end

+ 0
- 1
lib/ios/RNNStore.h View File

5
 #import "ReactNativeNavigation.h"
5
 #import "ReactNativeNavigation.h"
6
 
6
 
7
 typedef void (^RNNTransitionCompletionBlock)(void);
7
 typedef void (^RNNTransitionCompletionBlock)(void);
8
-//typedef void (^RNNTransitionRejectionBlock)(NSString *code, NSString *message, NSError *error);
9
 
8
 
10
 @interface RNNStore : NSObject
9
 @interface RNNStore : NSObject
11
 
10
 

+ 0
- 8
lib/ios/RNNTabBarController.m View File

41
 	}
41
 	}
42
 }
42
 }
43
 
43
 
44
-- (BOOL)isCustomTransitioned {
45
-	return NO;
46
-}
47
-
48
-- (RNNOptions *)options {
49
-	return nil;
50
-}
51
-
52
 - (void)mergeOptions:(NSDictionary *)options {
44
 - (void)mergeOptions:(NSDictionary *)options {
53
 	[((UIViewController<RNNRootViewProtocol>*)self.selectedViewController) mergeOptions:options];
45
 	[((UIViewController<RNNRootViewProtocol>*)self.selectedViewController) mergeOptions:options];
54
 }
46
 }

+ 0
- 8
lib/ios/RNNTopTabsViewController.m View File

72
     [super viewDidLoad];
72
     [super viewDidLoad];
73
 }
73
 }
74
 
74
 
75
-- (BOOL)isCustomTransitioned {
76
-	return NO;
77
-}
78
-
79
-- (RNNOptions *)options {
80
-	return nil;
81
-}
82
-
83
 - (NSString *)componentId {
75
 - (NSString *)componentId {
84
 	return _currentViewController.componentId;
76
 	return _currentViewController.componentId;
85
 }
77
 }

+ 75
- 2
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

4
 #import "RNNNavigationOptions.h"
4
 #import "RNNNavigationOptions.h"
5
 #import "RNNTestRootViewCreator.h"
5
 #import "RNNTestRootViewCreator.h"
6
 #import "RNNRootViewController.h"
6
 #import "RNNRootViewController.h"
7
-#import "RNNNavigationController.h"
7
+#import "RNNNavigationStackManager.h"
8
+
9
+@interface MockUINavigationController : UINavigationController
10
+@property (nonatomic, strong) NSArray* willReturnVCs;
11
+@end
12
+
13
+@implementation MockUINavigationController
14
+
15
+-(NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
16
+	return self.willReturnVCs;
17
+}
18
+
19
+-(NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
20
+	return self.willReturnVCs;
21
+}
22
+
23
+@end
8
 
24
 
9
 @interface RNNCommandsHandlerTest : XCTestCase
25
 @interface RNNCommandsHandlerTest : XCTestCase
10
 
26
 
11
 @property (nonatomic, strong) RNNStore* store;
27
 @property (nonatomic, strong) RNNStore* store;
12
 @property (nonatomic, strong) RNNCommandsHandler* uut;
28
 @property (nonatomic, strong) RNNCommandsHandler* uut;
29
+@property (nonatomic, strong) RNNRootViewController* vc1;
30
+@property (nonatomic, strong) RNNRootViewController* vc2;
31
+@property (nonatomic, strong) RNNRootViewController* vc3;
32
+@property (nonatomic, strong) MockUINavigationController* nvc;
13
 
33
 
14
 @end
34
 @end
15
 
35
 
20
 //	[self.store setReadyToReceiveCommands:true];
40
 //	[self.store setReadyToReceiveCommands:true];
21
 	self.store = [[RNNStore alloc] init];
41
 	self.store = [[RNNStore alloc] init];
22
 	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:nil eventEmitter:nil];
42
 	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:nil eventEmitter:nil];
43
+	self.vc1 = [RNNRootViewController new];
44
+	self.vc2 = [RNNRootViewController new];
45
+	self.vc3 = [RNNRootViewController new];
46
+	_nvc = [[MockUINavigationController alloc] init];
47
+	[_nvc setViewControllers:@[self.vc1, self.vc2, self.vc3]];
48
+	[self.store setComponent:self.vc1 componentId:@"vc1"];
49
+	[self.store setComponent:self.vc2 componentId:@"vc2"];
50
+	[self.store setComponent:self.vc3 componentId:@"vc3"];
23
 }
51
 }
24
 
52
 
25
 
53
 
40
 
68
 
41
 	[skipMethods addObject:@"initWithStore:controllerFactory:eventEmitter:"];
69
 	[skipMethods addObject:@"initWithStore:controllerFactory:eventEmitter:"];
42
 	[skipMethods addObject:@"assertReady"];
70
 	[skipMethods addObject:@"assertReady"];
71
+	[skipMethods addObject:@"removePopedViewControllers:"];
43
 	[skipMethods addObject:@".cxx_destruct"];
72
 	[skipMethods addObject:@".cxx_destruct"];
44
 
73
 
45
 	NSMutableArray* result = [NSMutableArray new];
74
 	NSMutableArray* result = [NSMutableArray new];
70
 															rootViewCreator:[[RNNTestRootViewCreator alloc] init]
99
 															rootViewCreator:[[RNNTestRootViewCreator alloc] init]
71
 															   eventEmitter:nil
100
 															   eventEmitter:nil
72
 														  isExternalComponent:NO];
101
 														  isExternalComponent:NO];
73
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:vc];
102
+	UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:vc];
74
 	[vc viewWillAppear:false];
103
 	[vc viewWillAppear:false];
75
 	XCTAssertTrue([vc.navigationItem.title isEqual:@"the title"]);
104
 	XCTAssertTrue([vc.navigationItem.title isEqual:@"the title"]);
76
 
105
 
86
 	}];
115
 	}];
87
 }
116
 }
88
 
117
 
118
+- (void)testPop_removeTopVCFromStore {
119
+	[self.store setReadyToReceiveCommands:true];
120
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
121
+
122
+	[self.uut pop:@"vc3" options:nil completion:^{
123
+		XCTAssertNil([self.store findComponentForId:@"vc3"]);
124
+		XCTAssertNotNil([self.store findComponentForId:@"vc2"]);
125
+		XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
126
+		[expectation fulfill];
127
+	} rejection:^(NSString *code, NSString *message, NSError *error) {
128
+		
129
+	}];
130
+	
131
+	[self waitForExpectationsWithTimeout:1 handler:nil];
132
+}
133
+
134
+- (void)testPopToSpecificVC_removeAllPopedVCFromStore {
135
+	[self.store setReadyToReceiveCommands:true];
136
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
137
+	_nvc.willReturnVCs = @[self.vc2, self.vc3];
138
+	[self.uut popTo:@"vc1" completion:^{
139
+		XCTAssertNil([self.store findComponentForId:@"vc2"]);
140
+		XCTAssertNil([self.store findComponentForId:@"vc3"]);
141
+		XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
142
+		[expectation fulfill];
143
+	} rejection:nil];
144
+	
145
+	[self waitForExpectationsWithTimeout:1 handler:nil];
146
+}
147
+
148
+- (void)testPopToRoot_removeAllTopVCsFromStore {
149
+	[self.store setReadyToReceiveCommands:true];
150
+	_nvc.willReturnVCs = @[self.vc2, self.vc3];
151
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
152
+	[self.uut popToRoot:@"vc3" completion:^{
153
+		XCTAssertNil([self.store findComponentForId:@"vc2"]);
154
+		XCTAssertNil([self.store findComponentForId:@"vc3"]);
155
+		XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
156
+		[expectation fulfill];
157
+	} rejection:nil];
158
+	
159
+	[self waitForExpectationsWithTimeout:1 handler:nil];
160
+}
161
+
89
 @end
162
 @end

+ 38
- 51
lib/ios/ReactNativeNavigationTests/RNNNavigationStackManagerTest.m View File

2
 #import "RNNStore.h"
2
 #import "RNNStore.h"
3
 #import "RNNNavigationStackManager.h"
3
 #import "RNNNavigationStackManager.h"
4
 
4
 
5
-@interface MockUINavigationController : UINavigationController
6
-@property (nonatomic, strong) NSArray* willReturnVCs;
7
-@end
8
-
9
-@implementation MockUINavigationController
10
-
11
--(NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
12
-	return self.willReturnVCs;
13
-}
14
-
15
--(NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
16
-	return self.willReturnVCs;
17
-}
18
-
19
-@end
20
-
21
 
5
 
22
 @interface RNNNavigationStackManagerTest : XCTestCase
6
 @interface RNNNavigationStackManagerTest : XCTestCase
23
 
7
 
24
 @property (nonatomic, strong) RNNStore *store;
8
 @property (nonatomic, strong) RNNStore *store;
25
-@property (nonatomic, strong) RNNNavigationStackManager *uut;
26
-@property (nonatomic, strong) MockUINavigationController *nvc;
9
+@property (nonatomic, strong) UINavigationController *nvc;
27
 @property (nonatomic, strong) UIViewController *vc1;
10
 @property (nonatomic, strong) UIViewController *vc1;
28
 @property (nonatomic, strong) UIViewController *vc2;
11
 @property (nonatomic, strong) UIViewController *vc2;
29
 @property (nonatomic, strong) UIViewController *vc3;
12
 @property (nonatomic, strong) UIViewController *vc3;
13
+@property (nonatomic, strong) RNNNavigationStackManager *stackManager;
30
 
14
 
31
 @end
15
 @end
32
 
16
 
35
 - (void)setUp {
19
 - (void)setUp {
36
     [super setUp];
20
     [super setUp];
37
 	self.store = [RNNStore new];
21
 	self.store = [RNNStore new];
38
-	self.uut = [[RNNNavigationStackManager alloc] initWithStore:self.store];
39
 	
22
 	
40
-	self.nvc = [[MockUINavigationController alloc] init];
23
+	self.nvc = [[UINavigationController alloc] init];
41
 	self.vc1 = [RNNRootViewController new];
24
 	self.vc1 = [RNNRootViewController new];
42
 	self.vc2 = [RNNRootViewController new];
25
 	self.vc2 = [RNNRootViewController new];
43
 	self.vc3 = [RNNRootViewController new];
26
 	self.vc3 = [RNNRootViewController new];
27
+	self.stackManager = [RNNNavigationStackManager new];
28
+	
44
 	NSArray *vcArray = @[self.vc1, self.vc2, self.vc3];
29
 	NSArray *vcArray = @[self.vc1, self.vc2, self.vc3];
45
 	[self.nvc setViewControllers:vcArray];
30
 	[self.nvc setViewControllers:vcArray];
46
-	
47
-	[self.store setComponent:self.vc1 componentId:@"vc1"];
48
-	[self.store setComponent:self.vc2 componentId:@"vc2"];
49
-	[self.store setComponent:self.vc3 componentId:@"vc3"];
50
-	
51
-	
52
 }
31
 }
53
 
32
 
54
 
33
 
55
-- (void)testPop_removeTopVCFromStore {
56
-	[self.uut pop:@"vc3" withTransitionOptions:nil rejection:nil];
57
-	XCTAssertNil([self.store findComponentForId:@"vc3"]);
58
-	XCTAssertNotNil([self.store findComponentForId:@"vc2"]);
59
-	XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
34
+- (void)testPop_removeTopVCFromStack {
35
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
36
+	XCTAssertTrue([self.nvc.topViewController isEqual:self.vc3]);
37
+	[_stackManager popTo:self.vc2 animated:YES completion:^(NSArray *poppedViewControllers) {
38
+		XCTAssertTrue([self.nvc.topViewController isEqual:self.vc2]);
39
+		[expectation fulfill];
40
+	} rejection:nil];
41
+	
42
+	[self waitForExpectationsWithTimeout:1 handler:nil];
60
 }
43
 }
61
 
44
 
62
-- (void)testPopToSpecificVC_removeAllPopedVCFromStore {
63
-	self.nvc.willReturnVCs = @[self.vc2, self.vc3];
64
-	[self.uut popTo:@"vc1" rejection:nil];
65
-	
66
-	XCTAssertNil([self.store findComponentForId:@"vc2"]);
67
-	XCTAssertNil([self.store findComponentForId:@"vc3"]);
68
-	XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
45
+- (void)testPopToSpecificVC_removeAllPopedVCFromStack {
46
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
47
+	XCTAssertFalse([self.nvc.topViewController isEqual:self.vc1]);
48
+	[_stackManager popTo:self.vc1 animated:NO completion:^(NSArray *poppedViewControllers) {
49
+		XCTAssertTrue([self.nvc.topViewController isEqual:self.vc1]);
50
+		[expectation fulfill];
51
+	} rejection:nil];
69
 	
52
 	
53
+	[self waitForExpectationsWithTimeout:1 handler:nil];
70
 }
54
 }
71
 
55
 
72
-- (void)testPopToRoot_removeAllTopVCsFromStore {
73
-	self.nvc.willReturnVCs = @[self.vc2, self.vc3];
74
-	[self.uut popToRoot:@"vc3" rejection:nil];
56
+- (void)testPopToRoot_removeAllTopVCsFromStack {
57
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
58
+	[_stackManager popToRoot:self.vc3 animated:NO completion:^(NSArray *poppedViewControllers) {
59
+		XCTAssertTrue(self.nvc.childViewControllers.count == 1);
60
+		XCTAssertTrue([self.nvc.topViewController isEqual:self.vc1]);
61
+		[expectation fulfill];
62
+	} rejection:nil];
75
 	
63
 	
76
-	XCTAssertNil([self.store findComponentForId:@"vc2"]);
77
-	XCTAssertNil([self.store findComponentForId:@"vc3"]);
78
-	XCTAssertNotNil([self.store findComponentForId:@"vc1"]);
79
-
64
+	[self waitForExpectationsWithTimeout:1 handler:nil];
80
 }
65
 }
81
 
66
 
82
 - (void)testStackRoot_shouldUpdateNavigationControllerChildrenViewControllers {
67
 - (void)testStackRoot_shouldUpdateNavigationControllerChildrenViewControllers {
83
-	self.nvc.willReturnVCs = @[self.vc1, self.vc2, self.vc3];
84
-	
85
-	[self.uut setStackRoot:self.vc2 fromComponent:@"vc1" completion:nil rejection:nil];
86
-	
87
-	XCTAssertEqual(self.nvc.viewControllers.lastObject, self.vc2);
88
-	XCTAssertEqual(self.nvc.viewControllers.count, 1);
68
+	XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method"];
69
+	[_stackManager setStackRoot:self.vc2 fromViewController:self.vc1 animated:NO completion:^{
70
+		XCTAssertTrue(self.nvc.childViewControllers.count == 1);
71
+		XCTAssertTrue([self.nvc.topViewController isEqual:self.vc2]);
72
+		[expectation fulfill];
73
+	} rejection:nil];
74
+
75
+	[self waitForExpectationsWithTimeout:1 handler:nil];
89
 }
76
 }
90
 
77
 
91
 @end
78
 @end