Browse Source

Handle adding and removing components from registry manually by presenters (#4942)

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

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

@@ -4,6 +4,8 @@
4 4
 
5 5
 @property (nonatomic, weak) id bindedViewController;
6 6
 
7
+@property (nonatomic, strong) NSString* bindedComponentId;
8
+
7 9
 - (void)bindViewController:(UIViewController *)bindedViewController;
8 10
 
9 11
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions;

+ 3
- 2
lib/ios/RNNBasePresenter.m View File

@@ -10,13 +10,14 @@
10 10
 
11 11
 @implementation RNNBasePresenter
12 12
 
13
-- (instancetype)initWithcomponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
13
+- (instancetype)initWithComponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
14 14
 	self = [super init];
15 15
 	self.componentRegistry = componentRegistry;
16 16
 	return self;
17 17
 }
18 18
 
19
-- (void)bindViewController:(UIViewController *)bindedViewController {
19
+- (void)bindViewController:(UIViewController<RNNLayoutProtocol> *)bindedViewController {
20
+	self.bindedComponentId = bindedViewController.layoutInfo.componentId;
20 21
 	_bindedViewController = bindedViewController;
21 22
 }
22 23
 

+ 1
- 1
lib/ios/RNNBridgeManager.m View File

@@ -92,7 +92,7 @@
92 92
 
93 93
 - (void)onJavaScriptWillLoad {
94 94
 	[_store clean];
95
-	[_componentRegistry clean];
95
+	[_componentRegistry clear];
96 96
 }
97 97
 
98 98
 - (void)onJavaScriptLoaded {

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

@@ -115,7 +115,7 @@
115 115
 - (UIViewController<RNNParentProtocol> *)createComponent:(RNNLayoutNode*)node {
116 116
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
117 117
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];;
118
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithcomponentRegistry:_componentRegistry];
118
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithComponentRegistry:_componentRegistry];
119 119
 	
120 120
 	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter presenter:presenter options:options defaultOptions:_defaultOptions];
121 121
 	
@@ -137,7 +137,7 @@
137 137
 
138 138
 
139 139
 - (UIViewController<RNNParentProtocol> *)createStack:(RNNLayoutNode*)node {
140
-	RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] initWithcomponentRegistry:_componentRegistry];
140
+	RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] initWithComponentRegistry:_componentRegistry];
141 141
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
142 142
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];;
143 143
 	

+ 1
- 1
lib/ios/RNNNavigationControllerPresenter.h View File

@@ -7,7 +7,7 @@
7 7
 
8 8
 @property (nonatomic, strong) InteractivePopGestureDelegate *interactivePopGestureDelegate;
9 9
 
10
-- (instancetype)initWithcomponentRegistry:(RNNReactComponentRegistry *)componentRegistry;
10
+- (instancetype)initWithComponentRegistry:(RNNReactComponentRegistry *)componentRegistry;
11 11
 
12 12
 - (void)applyOptionsBeforePopping:(RNNNavigationOptions *)options;
13 13
 

+ 11
- 10
lib/ios/RNNNavigationControllerPresenter.m View File

@@ -13,16 +13,12 @@
13 13
 @end
14 14
 @implementation RNNNavigationControllerPresenter
15 15
 
16
-- (instancetype)initWithcomponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
16
+- (instancetype)initWithComponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
17 17
 	self = [super init];
18 18
 	_componentRegistry = componentRegistry;
19 19
 	return self;
20 20
 }
21 21
 
22
-- (void)bindViewController:(UIViewController *)bindedViewController {
23
-	self.bindedViewController = bindedViewController;
24
-}
25
-
26 22
 - (void)applyOptions:(RNNNavigationOptions *)options {
27 23
 	[super applyOptions:options];
28 24
 	
@@ -179,7 +175,8 @@
179 175
 		readyBlock = nil;
180 176
 	}
181 177
 	if (options.topBar.component.name.hasValue) {
182
-		RCTRootView *reactView = [_componentRegistry createComponentIfNotExists:options.topBar.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
178
+		NSString* currentChildComponentId = [navigationController getCurrentChild].layoutInfo.componentId;
179
+		RCTRootView *reactView = [_componentRegistry createComponentIfNotExists:options.topBar.component parentComponentId:currentChildComponentId reactViewReadyBlock:readyBlock];
183 180
 		
184 181
 		if (_customTopBar) {
185 182
 			[_customTopBar removeFromSuperview];
@@ -190,6 +187,7 @@
190 187
 		[navigationController.navigationBar addSubview:_customTopBar];
191 188
 	} else {
192 189
 		[_customTopBar removeFromSuperview];
190
+		_customTopBar = nil;
193 191
 		if (readyBlock) {
194 192
 			readyBlock();
195 193
 		}
@@ -203,15 +201,19 @@
203 201
 		readyBlock = nil;
204 202
 	}
205 203
 	if (options.topBar.background.component.name.hasValue) {
206
-		RCTRootView *reactView = [_componentRegistry createComponentIfNotExists:options.topBar.background.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
204
+		NSString* currentChildComponentId = [navigationController getCurrentChild].layoutInfo.componentId;
205
+		RCTRootView *reactView = [_componentRegistry createComponentIfNotExists:options.topBar.background.component parentComponentId:currentChildComponentId reactViewReadyBlock:readyBlock];
207 206
 		
208 207
 		if (_customTopBarBackground) {
209 208
 			[_customTopBarBackground removeFromSuperview];
210 209
 		}
211
-		_customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
210
+		RNNCustomTitleView* customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
211
+		_customTopBarBackground = customTopBarBackground;
212
+		
212 213
 		[navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1];
213 214
 	} else {
214 215
 		[_customTopBarBackground removeFromSuperview];
216
+		_customTopBarBackground = nil;
215 217
 		if (readyBlock) {
216 218
 			readyBlock();
217 219
 		}
@@ -219,8 +221,7 @@
219 221
 }
220 222
 
221 223
 - (void)dealloc {
222
-	RNNNavigationController* navigationController = self.bindedViewController;
223
-	[_componentRegistry removeComponent:navigationController.layoutInfo.componentId];
224
+	[_componentRegistry removeComponent:self.bindedComponentId];
224 225
 }
225 226
 
226 227
 @end

+ 3
- 1
lib/ios/RNNReactComponentRegistry.h View File

@@ -12,6 +12,8 @@
12 12
 
13 13
 - (void)removeComponent:(NSString *)componentId;
14 14
 
15
-- (void)clean;
15
+- (void)clearComponentsForParentId:(NSString *)parentComponentId;
16
+
17
+- (void)clear;
16 18
 
17 19
 @end

+ 6
- 2
lib/ios/RNNReactComponentRegistry.m View File

@@ -12,7 +12,7 @@
12 12
 - (instancetype)initWithCreator:(id<RNNRootViewCreator>)creator {
13 13
 	self = [super init];
14 14
 	_creator = creator;
15
-	_componentStore = [NSMapTable strongToWeakObjectsMapTable];
15
+	_componentStore = [NSMapTable new];
16 16
 	return self;
17 17
 }
18 18
 
@@ -38,13 +38,17 @@
38 38
 	return [_componentStore objectForKey:parentComponentId];;
39 39
 }
40 40
 
41
+- (void)clearComponentsForParentId:(NSString *)parentComponentId {
42
+	[_componentStore removeObjectForKey:parentComponentId];;
43
+}
44
+
41 45
 - (void)removeComponent:(NSString *)componentId {
42 46
 	if ([_componentStore objectForKey:componentId]) {
43 47
 		[_componentStore removeObjectForKey:componentId];
44 48
 	}
45 49
 }
46 50
 
47
-- (void)clean {
51
+- (void)clear {
48 52
 	[_componentStore removeAllObjects];
49 53
 }
50 54
 

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

@@ -30,8 +30,8 @@
30 30
 }
31 31
 
32 32
 - (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize {
33
-	_rootViewDidChangeIntrinsicSize = rootViewDidChangeIntrinsicSize;
34
-	self.delegate = self;
33
+		_rootViewDidChangeIntrinsicSize = rootViewDidChangeIntrinsicSize;
34
+		self.delegate = self;
35 35
 }
36 36
 
37 37
 - (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView {

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

@@ -5,10 +5,6 @@
5 5
 
6 6
 @implementation RNNSplitViewControllerPresenter
7 7
 
8
-- (void)bindViewController:(UISplitViewController *)bindedViewController viewCreator:(id<RNNRootViewCreator>)creator {
9
-	self.bindedViewController = bindedViewController;
10
-}
11
-
12 8
 - (void)applyOptions:(RNNNavigationOptions *)options {
13 9
 	[super applyOptions:options];
14 10
 	

+ 1
- 0
lib/ios/RNNTopBarOptions.m View File

@@ -36,6 +36,7 @@
36 36
 	self.backButton = [[RNNBackButtonOptions alloc] initWithDict:dict[@"backButton"]];
37 37
 	self.leftButtonStyle = [[RNNButtonOptions alloc] initWithDict:dict[@"leftButtonStyle"]];
38 38
 	self.rightButtonStyle = [[RNNButtonOptions alloc] initWithDict:dict[@"rightButtonStyle"]];
39
+	self.component = [[RNNComponentOptions alloc] initWithDict:dict[@"component"]];
39 40
 	
40 41
 	if (self.leftButtonColor.hasValue) {
41 42
 		self.leftButtonStyle.color = self.leftButtonColor;

+ 1
- 1
lib/ios/RNNViewControllerPresenter.h View File

@@ -4,7 +4,7 @@
4 4
 
5 5
 @interface RNNViewControllerPresenter : RNNBasePresenter
6 6
 
7
-- (instancetype)initWithcomponentRegistry:(RNNReactComponentRegistry *)componentRegistry;
7
+- (instancetype)initWithComponentRegistry:(RNNReactComponentRegistry *)componentRegistry;
8 8
 
9 9
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
10 10
 

+ 9
- 4
lib/ios/RNNViewControllerPresenter.m View File

@@ -26,14 +26,14 @@
26 26
 	return self;
27 27
 }
28 28
 
29
-- (instancetype)initWithcomponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
29
+- (instancetype)initWithComponentRegistry:(RNNReactComponentRegistry *)componentRegistry {
30 30
 	self = [self init];
31 31
 	_componentRegistry = componentRegistry;
32 32
 	return self;
33 33
 }
34 34
 
35
-- (void)bindViewController:(UIViewController *)bindedViewController {
36
-	self.bindedViewController = bindedViewController;
35
+- (void)bindViewController:(UIViewController<RNNLayoutProtocol> *)bindedViewController {
36
+	[super bindViewController:bindedViewController];
37 37
 	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController componentRegistry:_componentRegistry];
38 38
 }
39 39
 
@@ -175,12 +175,13 @@
175 175
 	if (options.topBar.title.component.name.hasValue) {
176 176
 		_customTitleView = (RNNReactView*)[_componentRegistry createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
177 177
 		_customTitleView.backgroundColor = UIColor.clearColor;
178
+		
178 179
 		NSString* alignment = [options.topBar.title.component.alignment getWithDefaultValue:@""];
179 180
 		[_customTitleView setAlignment:alignment];
181
+		
180 182
 		BOOL isCenter = [alignment isEqualToString:@"center"];
181 183
 		__weak RNNReactView *weakTitleView = _customTitleView;
182 184
 		CGRect frame = viewController.navigationController.navigationBar.bounds;
183
-		[_customTitleView setFrame:frame];
184 185
 		[_customTitleView setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicContentSize) {
185 186
 			if (isCenter) {
186 187
 				[weakTitleView setFrame:CGRectMake(0, 0, intrinsicContentSize.width, intrinsicContentSize.height)];
@@ -214,6 +215,10 @@
214 215
 	}
215 216
 }
216 217
 
218
+- (void)dealloc {
219
+	[_componentRegistry clearComponentsForParentId:self.bindedComponentId];
220
+}
221
+
217 222
 - (void)cleanReactLeftovers {
218 223
 	_customTitleView = nil;
219 224
 }

+ 3
- 2
lib/ios/ReactNativeNavigationTests/RNNBasePresenterTest.m View File

@@ -2,12 +2,13 @@
2 2
 #import "RNNBasePresenter.h"
3 3
 #import <OCMock/OCMock.h>
4 4
 #import "UIViewController+RNNOptions.h"
5
+#import "RNNRootViewController.h"
5 6
 
6 7
 @interface RNNBottomTabPresenterTest : XCTestCase
7 8
 
8 9
 @property (nonatomic, strong) RNNBasePresenter *uut;
9 10
 @property (nonatomic, strong) RNNNavigationOptions *options;
10
-@property (nonatomic, strong) UIViewController* bindedViewController;
11
+@property (nonatomic, strong) RNNRootViewController* bindedViewController;
11 12
 @property (nonatomic, strong) id mockBindedViewController;
12 13
 
13 14
 @end
@@ -17,7 +18,7 @@
17 18
 - (void)setUp {
18 19
     [super setUp];
19 20
     self.uut = [[RNNBasePresenter alloc] init];
20
-	self.bindedViewController = [UIViewController new];
21
+	  self.bindedViewController = [RNNRootViewController new];
21 22
     self.mockBindedViewController = [OCMockObject partialMockForObject:self.bindedViewController];
22 23
     [self.uut bindViewController:self.mockBindedViewController];
23 24
     self.options = [[RNNNavigationOptions alloc] initEmptyOptions];

+ 2
- 1
lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m View File

@@ -2,6 +2,7 @@
2 2
 #import <OCMock/OCMock.h>
3 3
 #import "RNNTabBarPresenter.h"
4 4
 #import "UITabBarController+RNNOptions.h"
5
+#import "RNNTabBarController.h"
5 6
 
6 7
 @interface RNNTabBarPresenterTest : XCTestCase
7 8
 
@@ -16,7 +17,7 @@
16 17
 - (void)setUp {
17 18
     [super setUp];
18 19
 	self.uut = [[RNNTabBarPresenter alloc] init];
19
-	self.bindedViewController = [OCMockObject partialMockForObject:[UITabBarController new]];
20
+	self.bindedViewController = [OCMockObject partialMockForObject:[RNNTabBarController new]];
20 21
 	[self.uut bindViewController:self.bindedViewController];
21 22
 	self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
22 23
 }

+ 49
- 1
lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m View File

@@ -10,6 +10,7 @@
10 10
 @property (nonatomic, strong) RNNViewControllerPresenter *uut;
11 11
 @property (nonatomic, strong) RNNNavigationOptions *options;
12 12
 @property (nonatomic, strong) UIViewController *bindedViewController;
13
+@property (nonatomic, strong) RNNReactComponentRegistry *componentRegistry;
13 14
 
14 15
 @end
15 16
 
@@ -17,7 +18,8 @@
17 18
 
18 19
 - (void)setUp {
19 20
     [super setUp];
20
-	self.uut = [[RNNViewControllerPresenter alloc] init];
21
+	self.componentRegistry = [OCMockObject partialMockForObject:[RNNReactComponentRegistry new]];
22
+	self.uut = [[RNNViewControllerPresenter alloc] initWithComponentRegistry:self.componentRegistry];
21 23
 	self.bindedViewController = [OCMockObject partialMockForObject:[RNNRootViewController new]];
22 24
 	[self.uut bindViewController:self.bindedViewController];
23 25
 	self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
@@ -132,5 +134,51 @@
132 134
     [(id)self.bindedViewController verify];
133 135
 }
134 136
 
137
+- (void)testReactViewShouldBeReleasedOnDealloc {
138
+	RNNRootViewController* bindViewController = [RNNRootViewController new];
139
+	bindViewController.layoutInfo = [self createLayoutInfoWithComponentId:@"componentId"];
140
+	[self.uut bindViewController:bindViewController];
141
+	
142
+	self.options.topBar.title.component = [[RNNComponentOptions alloc] initWithDict:@{@"name": @"componentName"}];
143
+	
144
+	[[(id)self.componentRegistry expect] clearComponentsForParentId:self.uut.bindedComponentId];
145
+	self.uut = nil;
146
+	[(id)self.componentRegistry verify];
147
+}
148
+
149
+- (void)testBindViewControllerShouldSetBindedComponentId {
150
+	RNNRootViewController* bindViewController = [RNNRootViewController new];
151
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] init];
152
+	layoutInfo.componentId = @"componentId";
153
+	bindViewController.layoutInfo = layoutInfo;
154
+	
155
+	[self.uut bindViewController:bindViewController];
156
+	XCTAssertEqual(self.uut.bindedComponentId, @"componentId");
157
+}
158
+
159
+- (void)testRenderComponentsCreateReactViewWithBindedComponentId {
160
+	RNNRootViewController* bindedViewController = [RNNRootViewController new];
161
+	RNNLayoutInfo* layoutInfo = [self createLayoutInfoWithComponentId:@"componentId"];
162
+	bindedViewController.layoutInfo = layoutInfo;
163
+	
164
+	[self.uut bindViewController:bindedViewController];
165
+	
166
+	self.options.topBar.title.component = [[RNNComponentOptions alloc] initWithDict:@{@"name": @"titleComponent"}];
167
+	
168
+	[[(id)self.componentRegistry expect] createComponentIfNotExists:self.options.topBar.title.component parentComponentId:self.uut.bindedComponentId reactViewReadyBlock:[OCMArg any]];
169
+	[self.uut renderComponents:self.options perform:nil];
170
+	[(id)self.componentRegistry verify];
171
+	
172
+	
173
+	XCTAssertEqual(self.uut.bindedComponentId, @"componentId");
174
+}
175
+
176
+
177
+
178
+- (RNNLayoutInfo *)createLayoutInfoWithComponentId:(NSString *)componentId {
179
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] init];
180
+	layoutInfo.componentId = @"componentId";
181
+	return layoutInfo;
182
+}
135 183
 
136 184
 @end