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

Commit 9b51e30

Browse files
authored
iOS: Reduce engine/view controller coupling (#57151)
Eliminates some cases where `FlutterViewController` was relying on `FlutterEngine` internals: * `[FlutterEngine shell]` * `[FlutterEngine platformView]` * `[FlutterEngine iosPlatformView]` Instead, `FlutterEngine` now exposes: * `installFirstFrameCallback:` * `enableSemantics:withFlags:` * `notifyViewCreated` * `notifyViewDestroyed` * `waitForFirstFrameSync:callback:` Also fixes a couple cases where we were relying on transitive header includes: * `FlutterAppController` relied on `FlutterViewController_Internal.h` for `sendDeepLinkToFramework:completionHandler:` This is a refactoring followup to #57099 that introduces no semantic changes. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent a98f8c8 commit 9b51e30

File tree

10 files changed

+142
-119
lines changed

10 files changed

+142
-119
lines changed

shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
1111
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
1212
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h"
13+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
1314

1415
FLUTTER_ASSERT_ARC
1516

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,48 @@ - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)p
319319
self.platformView->DispatchPointerDataPacket(std::move(packet));
320320
}
321321

322-
- (fml::WeakPtr<flutter::PlatformView>)platformView {
323-
if (!_shell) {
324-
return {};
322+
- (void)installFirstFrameCallback:(void (^)(void))block {
323+
if (!self.platformView) {
324+
return;
325+
}
326+
327+
__weak FlutterEngine* weakSelf = self;
328+
self.platformView->SetNextFrameCallback([weakSelf, block] {
329+
FlutterEngine* strongSelf = weakSelf;
330+
if (!strongSelf) {
331+
return;
332+
}
333+
FML_DCHECK(strongSelf.platformTaskRunner);
334+
FML_DCHECK(strongSelf.rasterTaskRunner);
335+
FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
336+
// Get callback on raster thread and jump back to platform thread.
337+
strongSelf.platformTaskRunner->PostTask([block]() { block(); });
338+
});
339+
}
340+
341+
- (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
342+
if (!self.platformView) {
343+
return;
344+
}
345+
self.platformView->SetSemanticsEnabled(enabled);
346+
self.platformView->SetAccessibilityFeatures(flags);
347+
}
348+
349+
- (void)notifyViewCreated {
350+
if (!self.platformView) {
351+
return;
325352
}
326-
return _shell->GetPlatformView();
353+
self.platformView->NotifyCreated();
327354
}
328355

329-
- (flutter::PlatformViewIOS*)iosPlatformView {
356+
- (void)notifyViewDestroyed {
357+
if (!self.platformView) {
358+
return;
359+
}
360+
self.platformView->NotifyDestroyed();
361+
}
362+
363+
- (flutter::PlatformViewIOS*)platformView {
330364
if (!_shell) {
331365
return nullptr;
332366
}
@@ -402,16 +436,16 @@ - (void)sendKeyEvent:(const FlutterKeyEvent&)event
402436
}
403437

404438
- (void)ensureSemanticsEnabled {
405-
if (!self.iosPlatformView) {
439+
if (!self.platformView) {
406440
return;
407441
}
408-
self.iosPlatformView->SetSemanticsEnabled(true);
442+
self.platformView->SetSemanticsEnabled(true);
409443
}
410444

411445
- (void)setViewController:(FlutterViewController*)viewController {
412-
FML_DCHECK(self.iosPlatformView);
446+
FML_DCHECK(self.platformView);
413447
_viewController = viewController;
414-
self.iosPlatformView->SetOwnerViewController(_viewController);
448+
self.platformView->SetOwnerViewController(_viewController);
415449
[self maybeSetupPlatformViewChannels];
416450
[self updateDisplays];
417451
self.textInputPlugin.viewController = viewController;
@@ -432,8 +466,8 @@ - (void)setViewController:(FlutterViewController*)viewController {
432466
}
433467

434468
- (void)attachView {
435-
FML_DCHECK(self.iosPlatformView);
436-
self.iosPlatformView->attachView();
469+
FML_DCHECK(self.platformView);
470+
self.platformView->attachView();
437471
}
438472

439473
- (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
@@ -451,11 +485,8 @@ - (void)notifyViewControllerDeallocated {
451485
self.textInputPlugin.viewController = nil;
452486
if (!self.allowHeadlessExecution) {
453487
[self destroyContext];
454-
} else if (_shell) {
455-
flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
456-
if (platform_view) {
457-
platform_view->SetOwnerViewController({});
458-
}
488+
} else if (self.platformView) {
489+
self.platformView->SetOwnerViewController({});
459490
}
460491
[self.textInputPlugin resetViewResponder];
461492
_viewController = nil;
@@ -1215,8 +1246,8 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channe
12151246
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
12161247
NSParameterAssert(channel);
12171248
if (_shell && _shell->IsSetup()) {
1218-
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1219-
handler, taskQueue);
1249+
self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1250+
handler, taskQueue);
12201251
return _connections->AquireConnection(channel.UTF8String);
12211252
} else {
12221253
NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
@@ -1229,18 +1260,18 @@ - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
12291260
if (_shell && _shell->IsSetup()) {
12301261
std::string channel = _connections->CleanupConnection(connection);
12311262
if (!channel.empty()) {
1232-
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1233-
nil);
1263+
self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1264+
nil);
12341265
}
12351266
}
12361267
}
12371268

12381269
#pragma mark - FlutterTextureRegistry
12391270

12401271
- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
1241-
FML_DCHECK(self.iosPlatformView);
1272+
FML_DCHECK(self.platformView);
12421273
int64_t textureId = self.nextTextureId++;
1243-
self.iosPlatformView->RegisterExternalTexture(textureId, texture);
1274+
self.platformView->RegisterExternalTexture(textureId, texture);
12441275
return textureId;
12451276
}
12461277

@@ -1350,6 +1381,13 @@ - (void)onLocaleUpdated:(NSNotification*)notification {
13501381
[self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
13511382
}
13521383

1384+
- (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1385+
callback:(NS_NOESCAPE void (^_Nonnull)(BOOL didTimeout))callback {
1386+
fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1387+
fml::Status status = self.shell.WaitForFirstFrame(waitTime);
1388+
callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1389+
}
1390+
13531391
- (void)waitForFirstFrame:(NSTimeInterval)timeout
13541392
callback:(void (^_Nonnull)(BOOL didTimeout))callback {
13551393
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
@@ -1364,8 +1402,8 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout
13641402
}
13651403

13661404
fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1367-
didTimeout =
1368-
strongSelf.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
1405+
fml::Status status = strongSelf.shell.WaitForFirstFrame(waitTime);
1406+
didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
13691407
});
13701408

13711409
// Only execute the main queue task once the background task has completely finished executing.

shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "flutter/fml/message_loop.h"
1212
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
1313
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
14+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
1415
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
1516

1617
FLUTTER_ASSERT_ARC
@@ -101,7 +102,7 @@ - (void)testCallsNotifyLowMemory {
101102
XCTAssertNotNil(engine);
102103
id mockEngine = OCMPartialMock(engine);
103104
OCMStub([mockEngine notifyLowMemory]);
104-
OCMStub([mockEngine iosPlatformView]).andReturn(platform_view.get());
105+
OCMStub([mockEngine platformView]).andReturn(platform_view.get());
105106

106107
[engine setViewController:nil];
107108
OCMVerify([mockEngine notifyLowMemory]);

shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ - (void)testShellGetters {
7575
XCTAssertNotNil(engine);
7676

7777
// Ensure getters don't deref _shell when it's null, and instead return nullptr.
78-
XCTAssertEqual(engine.platformView.get(), nullptr);
79-
XCTAssertEqual(engine.iosPlatformView, nullptr);
8078
XCTAssertEqual(engine.platformTaskRunner.get(), nullptr);
8179
XCTAssertEqual(engine.uiTaskRunner.get(), nullptr);
8280
XCTAssertEqual(engine.rasterTaskRunner.get(), nullptr);
@@ -435,10 +433,10 @@ - (void)testSpawnsShareGpuContext {
435433
initialRoute:nil
436434
entrypointArgs:nil];
437435
XCTAssertNotNil(spawn);
438-
XCTAssertTrue([engine iosPlatformView] != nullptr);
439-
XCTAssertTrue([spawn iosPlatformView] != nullptr);
440-
std::shared_ptr<flutter::IOSContext> engine_context = [engine iosPlatformView]->GetIosContext();
441-
std::shared_ptr<flutter::IOSContext> spawn_context = [spawn iosPlatformView]->GetIosContext();
436+
XCTAssertTrue(engine.platformView != nullptr);
437+
XCTAssertTrue(spawn.platformView != nullptr);
438+
std::shared_ptr<flutter::IOSContext> engine_context = engine.platformView->GetIosContext();
439+
std::shared_ptr<flutter::IOSContext> spawn_context = spawn.platformView->GetIosContext();
442440
XCTAssertEqual(engine_context, spawn_context);
443441
}
444442

shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,23 @@
2525
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h"
2626
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
2727
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
28-
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
28+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
2929

3030
NS_ASSUME_NONNULL_BEGIN
3131

3232
@interface FlutterEngine () <FlutterViewEngineDelegate>
3333

34-
- (flutter::Shell&)shell;
35-
3634
- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics;
3735
- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet;
3836

3937
- (fml::RefPtr<fml::TaskRunner>)platformTaskRunner;
4038
- (fml::RefPtr<fml::TaskRunner>)uiTaskRunner;
4139
- (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner;
4240

43-
- (fml::WeakPtr<flutter::PlatformView>)platformView;
41+
- (void)installFirstFrameCallback:(void (^)(void))block;
42+
- (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags;
43+
- (void)notifyViewCreated;
44+
- (void)notifyViewDestroyed;
4445

4546
- (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type
4647
base64Encode:(bool)base64Encode;
@@ -56,8 +57,13 @@ NS_ASSUME_NONNULL_BEGIN
5657
initialRoute:(nullable NSString*)initialRoute;
5758
- (void)attachView;
5859
- (void)notifyLowMemory;
59-
- (flutter::PlatformViewIOS*)iosPlatformView;
6060

61+
/// Blocks until the first frame is presented or the timeout is exceeded, then invokes callback.
62+
- (void)waitForFirstFrameSync:(NSTimeInterval)timeout
63+
callback:(NS_NOESCAPE void (^)(BOOL didTimeout))callback;
64+
65+
/// Asynchronously waits until the first frame is presented or the timeout is exceeded, then invokes
66+
/// callback.
6167
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
6268

6369
/**

shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#import "flutter/shell/common/shell.h"
99
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
1010
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
11+
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
1112
#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
1213
#include "flutter/shell/platform/embedder/embedder.h"
1314

@@ -24,6 +25,8 @@ class ThreadHost;
2425
@property(readonly, nonatomic) BOOL enableEmbedderAPI;
2526

2627
- (flutter::Shell&)shell;
28+
- (flutter::PlatformViewIOS*)platformView;
29+
2730
- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger;
2831
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI;
2932
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "flutter/fml/thread.h"
1717
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
1818
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
19-
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
19+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
2020
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
2121
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
2222
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h"
@@ -4319,8 +4319,8 @@ - (void)testLayerPool {
43194319
// Create an IOSContext and GrDirectContext.
43204320
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
43214321
[engine run];
4322-
XCTAssertTrue([engine iosPlatformView] != nullptr);
4323-
auto ios_context = [engine iosPlatformView]->GetIosContext();
4322+
XCTAssertTrue(engine.platformView != nullptr);
4323+
auto ios_context = engine.platformView->GetIosContext();
43244324
auto gr_context = ios_context->GetMainContext();
43254325

43264326
auto pool = flutter::OverlayLayerPool{};

0 commit comments

Comments
 (0)