Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit fb75e54

Browse files
committed
fix present time race that can occur between viewWillAppear and viewDidAppear
1 parent c3bfbae commit fb75e54

File tree

3 files changed

+73
-53
lines changed

3 files changed

+73
-53
lines changed

testing/scenario_app/ios/Scenarios/Scenarios/ScreenBeforeFlutter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
@interface ScreenBeforeFlutter : UIViewController
88

99
- (id)initWithEngineRunCompletion:(void (^)(void))engineRunCompletion;
10-
- (FlutterViewController*)showFlutter;
10+
- (FlutterViewController*)showFlutter:(void (^)(void))showCompletion;
1111

1212
@property(nonatomic, readonly) FlutterEngine* engine;
1313

testing/scenario_app/ios/Scenarios/Scenarios/ScreenBeforeFlutter.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ - (void)viewDidLoad {
2727
showFlutterButton.tintColor = UIColor.whiteColor;
2828
showFlutterButton.clipsToBounds = YES;
2929
[showFlutterButton addTarget:self
30-
action:@selector(showFlutter)
30+
action:@selector(showFlutter:)
3131
forControlEvents:UIControlEventTouchUpInside];
3232

3333
[self.view addSubview:showFlutterButton];
@@ -39,11 +39,11 @@ - (void)viewDidLoad {
3939
[_engine runWithEntrypoint:nil];
4040
}
4141

42-
- (FlutterViewController*)showFlutter {
42+
- (FlutterViewController*)showFlutter:(void (^)(void))showCompletion {
4343
FlutterViewController* flutterVC = [[FlutterViewController alloc] initWithEngine:_engine
4444
nibName:nil
4545
bundle:nil];
46-
[self presentViewController:flutterVC animated:NO completion:nil];
46+
[self presentViewController:flutterVC animated:NO completion:showCompletion];
4747
return flutterVC;
4848
}
4949

testing/scenario_app/ios/Scenarios/ScenariosTests/AppLifecycleTests.m

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,34 @@ - (void)testDismissedFlutterViewControllerNotRespondingToApplicationLifecycle {
6464
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed"
6565
forStep:@"showing a FlutterViewController"]
6666
]];
67+
68+
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
69+
if (lifecycleExpectations.count == 0) {
70+
XCTFail(@"Unexpected lifecycle transition: %@", message);
71+
return;
72+
}
73+
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
74+
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
75+
XCTFail(@"Expected lifecycle %@ but instead received %@",
76+
[nextExpectation expectedLifecycle], message);
77+
return;
78+
}
79+
80+
[nextExpectation fulfill];
81+
[lifecycleExpectations removeObjectAtIndex:0];
82+
}];
6783

6884
FlutterViewController* flutterVC;
6985
@autoreleasepool {
7086
// Holding onto this FlutterViewController is consequential here. Since a released
7187
// FlutterViewController wouldn't keep listening to the application lifecycle events and produce
7288
// false positives for the application lifecycle tests further below.
73-
flutterVC = [rootVC showFlutter];
74-
NSLog(@"FlutterViewController instance %@ created", flutterVC);
75-
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
76-
if (lifecycleExpectations.count == 0) {
77-
XCTFail(@"Unexpected lifecycle transition: %@", message);
78-
return;
79-
}
80-
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
81-
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
82-
XCTFail(@"Expected lifecycle %@ but instead received %@",
83-
[nextExpectation expectedLifecycle], message);
84-
return;
85-
}
86-
87-
[nextExpectation fulfill];
88-
[lifecycleExpectations removeObjectAtIndex:0];
89+
XCTestExpectation* vcShown = [self expectationWithDescription:@"present"];
90+
flutterVC = [rootVC showFlutter:^{
91+
[vcShown fulfill];
8992
}];
93+
[self waitForExpectationsWithTimeout:5.0 handler:nil];
94+
NSLog(@"FlutterViewController instance %@ created", flutterVC);
9095

9196
// The expectations list isn't dequeued by the message handler yet.
9297
[self waitForExpectations:lifecycleExpectations timeout:5 enforceOrder:YES];
@@ -147,7 +152,11 @@ - (void)testDismissedFlutterViewControllerNotRespondingToApplicationLifecycle {
147152
]];
148153

149154
@autoreleasepool {
150-
flutterVC = [rootVC showFlutter];
155+
XCTestExpectation* vcShown = [self expectationWithDescription:@"present"];
156+
flutterVC = [rootVC showFlutter:^{
157+
[vcShown fulfill];
158+
}];
159+
[self waitForExpectationsWithTimeout:5.0 handler:nil];
151160
NSLog(@"FlutterViewController instance %@ created", flutterVC);
152161
[self waitForExpectations:lifecycleExpectations timeout:5 enforceOrder:YES];
153162

@@ -199,26 +208,31 @@ - (void)testVisibleFlutterViewControllerRespondsToApplicationLifecycle {
199208
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed"
200209
forStep:@"showing a FlutterViewController"]
201210
]];
211+
212+
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
213+
if (lifecycleExpectations.count == 0) {
214+
XCTFail(@"Unexpected lifecycle transition: %@", message);
215+
return;
216+
}
217+
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
218+
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
219+
XCTFail(@"Expected lifecycle %@ but instead received %@",
220+
[nextExpectation expectedLifecycle], message);
221+
return;
222+
}
223+
224+
[nextExpectation fulfill];
225+
[lifecycleExpectations removeObjectAtIndex:0];
226+
}];
202227

203228
FlutterViewController* flutterVC;
204229
@autoreleasepool {
205-
flutterVC = [rootVC showFlutter];
206-
NSLog(@"FlutterViewController instance %@ created", flutterVC);
207-
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
208-
if (lifecycleExpectations.count == 0) {
209-
XCTFail(@"Unexpected lifecycle transition: %@", message);
210-
return;
211-
}
212-
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
213-
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
214-
XCTFail(@"Expected lifecycle %@ but instead received %@",
215-
[nextExpectation expectedLifecycle], message);
216-
return;
217-
}
218-
219-
[nextExpectation fulfill];
220-
[lifecycleExpectations removeObjectAtIndex:0];
230+
XCTestExpectation* vcShown = [self expectationWithDescription:@"present"];
231+
flutterVC = [rootVC showFlutter:^{
232+
[vcShown fulfill];
221233
}];
234+
[self waitForExpectationsWithTimeout:5.0 handler:nil];
235+
NSLog(@"FlutterViewController instance %@ created", flutterVC);
222236

223237
[self waitForExpectations:lifecycleExpectations timeout:5];
224238

@@ -304,27 +318,33 @@ - (void)testFlutterViewControllerDetachingSendsApplicationLifecycle {
304318
[[XCAppLifecycleTestExpectation alloc] initForLifecycle:@"AppLifecycleState.resumed"
305319
forStep:@"showing a FlutterViewController"]
306320
]];
321+
322+
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
323+
if (lifecycleExpectations.count == 0) {
324+
XCTFail(@"Unexpected lifecycle transition: %@", message);
325+
return;
326+
}
327+
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
328+
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
329+
XCTFail(@"Expected lifecycle %@ but instead received %@",
330+
[nextExpectation expectedLifecycle], message);
331+
return;
332+
}
333+
334+
[nextExpectation fulfill];
335+
[lifecycleExpectations removeObjectAtIndex:0];
336+
}];
337+
307338
// At the end of Flutter VC, we want to make sure it deallocs and sends detached signal.
308339
// Using autoreleasepool will guarantee that.
309340
FlutterViewController* flutterVC;
310341
@autoreleasepool {
311-
flutterVC = [rootVC showFlutter];
312-
NSLog(@"FlutterViewController instance %@ created", flutterVC);
313-
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
314-
if (lifecycleExpectations.count == 0) {
315-
XCTFail(@"Unexpected lifecycle transition: %@", message);
316-
return;
317-
}
318-
XCAppLifecycleTestExpectation* nextExpectation = [lifecycleExpectations objectAtIndex:0];
319-
if (![[nextExpectation expectedLifecycle] isEqualToString:message]) {
320-
XCTFail(@"Expected lifecycle %@ but instead received %@",
321-
[nextExpectation expectedLifecycle], message);
322-
return;
323-
}
324-
325-
[nextExpectation fulfill];
326-
[lifecycleExpectations removeObjectAtIndex:0];
342+
XCTestExpectation* vcShown = [self expectationWithDescription:@"present"];
343+
flutterVC = [rootVC showFlutter:^{
344+
[vcShown fulfill];
327345
}];
346+
[self waitForExpectationsWithTimeout:5.0 handler:nil];
347+
NSLog(@"FlutterViewController instance %@ created", flutterVC);
328348

329349
[self waitForExpectations:lifecycleExpectations timeout:5];
330350

0 commit comments

Comments
 (0)