Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Reduce engine/view controller coupling #57151

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"

FLUTTER_ASSERT_ARC

Expand Down
86 changes: 62 additions & 24 deletions shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -319,14 +319,48 @@ - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)p
self.platformView->DispatchPointerDataPacket(std::move(packet));
}

- (fml::WeakPtr<flutter::PlatformView>)platformView {
if (!_shell) {
return {};
- (void)installFirstFrameCallback:(void (^)(void))block {
Copy link
Member Author

@cbracken cbracken Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below: moved from FlutterViewController basically verbatim.

if (!self.platformView) {
return;
}

__weak FlutterEngine* weakSelf = self;
self.platformView->SetNextFrameCallback([weakSelf, block] {
FlutterEngine* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
FML_DCHECK(strongSelf.platformTaskRunner);
FML_DCHECK(strongSelf.rasterTaskRunner);
FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
// Get callback on raster thread and jump back to platform thread.
strongSelf.platformTaskRunner->PostTask([block]() { block(); });
});
}

- (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
if (!self.platformView) {
return;
}
self.platformView->SetSemanticsEnabled(enabled);
self.platformView->SetAccessibilityFeatures(flags);
}

- (void)notifyViewCreated {
if (!self.platformView) {
return;
}
return _shell->GetPlatformView();
self.platformView->NotifyCreated();
}

- (flutter::PlatformViewIOS*)iosPlatformView {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eliminated this. Now we just use platformView. There's no real reason to have two different getters that return exactly the same object except casted to the subclass in one and the superclass in the other.

- (void)notifyViewDestroyed {
if (!self.platformView) {
return;
}
self.platformView->NotifyDestroyed();
}

- (flutter::PlatformViewIOS*)platformView {
if (!_shell) {
return nullptr;
}
Expand Down Expand Up @@ -402,16 +436,16 @@ - (void)sendKeyEvent:(const FlutterKeyEvent&)event
}

- (void)ensureSemanticsEnabled {
if (!self.iosPlatformView) {
if (!self.platformView) {
return;
}
self.iosPlatformView->SetSemanticsEnabled(true);
self.platformView->SetSemanticsEnabled(true);
}

- (void)setViewController:(FlutterViewController*)viewController {
FML_DCHECK(self.iosPlatformView);
FML_DCHECK(self.platformView);
_viewController = viewController;
self.iosPlatformView->SetOwnerViewController(_viewController);
self.platformView->SetOwnerViewController(_viewController);
[self maybeSetupPlatformViewChannels];
[self updateDisplays];
self.textInputPlugin.viewController = viewController;
Expand All @@ -432,8 +466,8 @@ - (void)setViewController:(FlutterViewController*)viewController {
}

- (void)attachView {
FML_DCHECK(self.iosPlatformView);
self.iosPlatformView->attachView();
FML_DCHECK(self.platformView);
self.platformView->attachView();
}

- (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
Expand All @@ -451,11 +485,8 @@ - (void)notifyViewControllerDeallocated {
self.textInputPlugin.viewController = nil;
if (!self.allowHeadlessExecution) {
[self destroyContext];
} else if (_shell) {
flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
if (platform_view) {
platform_view->SetOwnerViewController({});
}
} else if (self.platformView) {
self.platformView->SetOwnerViewController({});
}
[self.textInputPlugin resetViewResponder];
_viewController = nil;
Expand Down Expand Up @@ -1215,8 +1246,8 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channe
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
NSParameterAssert(channel);
if (_shell && _shell->IsSetup()) {
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
handler, taskQueue);
self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
handler, taskQueue);
return _connections->AquireConnection(channel.UTF8String);
} else {
NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
Expand All @@ -1229,18 +1260,18 @@ - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
if (_shell && _shell->IsSetup()) {
std::string channel = _connections->CleanupConnection(connection);
if (!channel.empty()) {
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
nil);
self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
nil);
}
}
}

#pragma mark - FlutterTextureRegistry

- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
FML_DCHECK(self.iosPlatformView);
FML_DCHECK(self.platformView);
int64_t textureId = self.nextTextureId++;
self.iosPlatformView->RegisterExternalTexture(textureId, texture);
self.platformView->RegisterExternalTexture(textureId, texture);
return textureId;
}

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

- (void)waitForFirstFrameSync:(NSTimeInterval)timeout
callback:(NS_NOESCAPE void (^_Nonnull)(BOOL didTimeout))callback {
fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
fml::Status status = self.shell.WaitForFirstFrame(waitTime);
callback(status.code() == fml::StatusCode::kDeadlineExceeded);
}

- (void)waitForFirstFrame:(NSTimeInterval)timeout
callback:(void (^_Nonnull)(BOOL didTimeout))callback {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
Expand All @@ -1364,8 +1402,8 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout
}

fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
didTimeout =
strongSelf.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
fml::Status status = strongSelf.shell.WaitForFirstFrame(waitTime);
didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
});

// Only execute the main queue task once the background task has completely finished executing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "flutter/fml/message_loop.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"

FLUTTER_ASSERT_ARC
Expand Down Expand Up @@ -101,7 +102,7 @@ - (void)testCallsNotifyLowMemory {
XCTAssertNotNil(engine);
id mockEngine = OCMPartialMock(engine);
OCMStub([mockEngine notifyLowMemory]);
OCMStub([mockEngine iosPlatformView]).andReturn(platform_view.get());
OCMStub([mockEngine platformView]).andReturn(platform_view.get());

[engine setViewController:nil];
OCMVerify([mockEngine notifyLowMemory]);
Expand Down
10 changes: 4 additions & 6 deletions shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ - (void)testShellGetters {
XCTAssertNotNil(engine);

// Ensure getters don't deref _shell when it's null, and instead return nullptr.
XCTAssertEqual(engine.platformView.get(), nullptr);
XCTAssertEqual(engine.iosPlatformView, nullptr);
XCTAssertEqual(engine.platformTaskRunner.get(), nullptr);
XCTAssertEqual(engine.uiTaskRunner.get(), nullptr);
XCTAssertEqual(engine.rasterTaskRunner.get(), nullptr);
Expand Down Expand Up @@ -435,10 +433,10 @@ - (void)testSpawnsShareGpuContext {
initialRoute:nil
entrypointArgs:nil];
XCTAssertNotNil(spawn);
XCTAssertTrue([engine iosPlatformView] != nullptr);
XCTAssertTrue([spawn iosPlatformView] != nullptr);
std::shared_ptr<flutter::IOSContext> engine_context = [engine iosPlatformView]->GetIosContext();
std::shared_ptr<flutter::IOSContext> spawn_context = [spawn iosPlatformView]->GetIosContext();
XCTAssertTrue(engine.platformView != nullptr);
XCTAssertTrue(spawn.platformView != nullptr);
std::shared_ptr<flutter::IOSContext> engine_context = engine.platformView->GetIosContext();
std::shared_ptr<flutter::IOSContext> spawn_context = spawn.platformView->GetIosContext();
XCTAssertEqual(engine_context, spawn_context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,23 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉


NS_ASSUME_NONNULL_BEGIN

@interface FlutterEngine () <FlutterViewEngineDelegate>

- (flutter::Shell&)shell;

- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics;
- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet;

- (fml::RefPtr<fml::TaskRunner>)platformTaskRunner;
- (fml::RefPtr<fml::TaskRunner>)uiTaskRunner;
- (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner;

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

- (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type
base64Encode:(bool)base64Encode;
Expand All @@ -56,8 +57,13 @@ NS_ASSUME_NONNULL_BEGIN
initialRoute:(nullable NSString*)initialRoute;
- (void)attachView;
- (void)notifyLowMemory;
- (flutter::PlatformViewIOS*)iosPlatformView;

/// Blocks until the first frame is presented or the timeout is exceeded, then invokes callback.
- (void)waitForFirstFrameSync:(NSTimeInterval)timeout
callback:(NS_NOESCAPE void (^)(BOOL didTimeout))callback;

/// Asynchronously waits until the first frame is presented or the timeout is exceeded, then invokes
/// callback.
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import "flutter/shell/common/shell.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
#include "flutter/shell/platform/embedder/embedder.h"

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

- (flutter::Shell&)shell;
- (flutter::PlatformViewIOS*)platformView;

- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger;
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI;
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "flutter/fml/thread.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h"
Expand Down Expand Up @@ -4319,8 +4319,8 @@ - (void)testLayerPool {
// Create an IOSContext and GrDirectContext.
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
[engine run];
XCTAssertTrue([engine iosPlatformView] != nullptr);
auto ios_context = [engine iosPlatformView]->GetIosContext();
XCTAssertTrue(engine.platformView != nullptr);
auto ios_context = engine.platformView->GetIosContext();
auto gr_context = ios_context->GetMainContext();

auto pool = flutter::OverlayLayerPool{};
Expand Down
Loading
Loading