5 #include <UIKit/UIKit.h>
6 #include "common/settings.h"
7 #define FML_USED_ON_EMBEDDER
13 #include "flutter/common/constants.h"
14 #include "flutter/fml/message_loop.h"
15 #include "flutter/fml/platform/darwin/platform_version.h"
16 #include "flutter/fml/trace_event.h"
17 #include "flutter/runtime/ptrace_check.h"
18 #include "flutter/shell/common/engine.h"
19 #include "flutter/shell/common/platform_view.h"
20 #include "flutter/shell/common/shell.h"
21 #include "flutter/shell/common/switches.h"
22 #include "flutter/shell/common/thread_host.h"
23 #include "flutter/shell/common/variable_refresh_rate_display.h"
24 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
27 #import "flutter/shell/platform/darwin/ios/InternalFlutterSwift/InternalFlutterSwift.h"
45 #include "flutter/shell/profiling/sampling_profiler.h"
53 fml::Thread::SetCurrentThreadName(config);
56 switch (config.priority) {
57 case fml::Thread::ThreadPriority::kBackground: {
58 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
59 [[NSThread currentThread] setThreadPriority:0];
62 case fml::Thread::ThreadPriority::kNormal: {
63 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
64 [[NSThread currentThread] setThreadPriority:0.5];
67 case fml::Thread::ThreadPriority::kRaster:
68 case fml::Thread::ThreadPriority::kDisplay: {
69 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
70 [[NSThread currentThread] setThreadPriority:1.0];
73 pthread_t thread = pthread_self();
74 if (!pthread_getschedparam(thread, &policy, ¶m)) {
75 param.sched_priority = 50;
76 pthread_setschedparam(thread, policy, ¶m);
83 #pragma mark - Public exported constants
88 #pragma mark - Internal constants
104 #pragma mark - Properties
107 @property(nonatomic, readonly, copy) NSString* labelPrefix;
108 @property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
109 @property(nonatomic, readonly, assign) BOOL restorationEnabled;
115 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
116 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
118 @property(nonatomic, readwrite, copy) NSString*
isolateId;
119 @property(nonatomic, copy) NSString* initialRoute;
120 @property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
122 @property(nonatomic, strong) FlutterConnectionCollection* connections;
123 @property(nonatomic, assign) int64_t nextTextureId;
125 #pragma mark - Channel properties
147 #pragma mark - Embedder API properties
156 std::shared_ptr<flutter::ThreadHost> _threadHost;
166 - (int64_t)engineIdentifier {
167 return reinterpret_cast<int64_t
>((__bridge
void*)
self);
170 - (instancetype)init {
171 return [
self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
174 - (instancetype)initWithName:(NSString*)labelPrefix {
175 return [
self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
178 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
179 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
182 - (instancetype)initWithName:(NSString*)labelPrefix
184 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
185 return [
self initWithName:labelPrefix
187 allowHeadlessExecution:allowHeadlessExecution
188 restorationEnabled:NO];
191 - (instancetype)initWithName:(NSString*)labelPrefix
193 allowHeadlessExecution:(BOOL)allowHeadlessExecution
194 restorationEnabled:(BOOL)restorationEnabled {
196 NSAssert(
self,
@"Super init cannot be nil");
197 NSAssert(labelPrefix,
@"labelPrefix is required");
200 _allowHeadlessExecution = allowHeadlessExecution;
201 _labelPrefix = [labelPrefix copy];
204 _enableEmbedderAPI = _dartProject.
settings.enable_embedder_api;
205 if (_enableEmbedderAPI) {
206 NSLog(
@"============== iOS: enable_embedder_api is on ==============");
207 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
208 FlutterEngineGetProcAddresses(&_embedderAPI);
211 if (!EnableTracingIfNecessary(_dartProject.settings)) {
213 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
214 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
215 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
216 @"profile and release mode apps can be launched from the home screen.");
220 _pluginPublications = [[NSMutableDictionary alloc] init];
221 _registrars = [[NSMutableDictionary alloc] init];
222 [
self recreatePlatformViewsController];
225 _connections = [[FlutterConnectionCollection alloc] init];
227 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
228 [center addObserver:self
229 selector:@selector(onMemoryWarning:)
230 name:UIApplicationDidReceiveMemoryWarningNotification
233 [
self setUpLifecycleNotifications:center];
235 [center addObserver:self
236 selector:@selector(onLocaleUpdated:)
237 name:NSCurrentLocaleDidChangeNotification
244 NSAssert([[NSThread currentThread] isMainThread],
@"Must be called on the main thread.");
245 return (__bridge
FlutterEngine*)
reinterpret_cast<void*
>(identifier);
248 - (void)setUpLifecycleNotifications:(NSNotificationCenter*)center {
251 [center addObserver:self
252 selector:@selector(sceneWillEnterForeground:)
253 name:UISceneWillEnterForegroundNotification
255 [center addObserver:self
256 selector:@selector(sceneDidEnterBackground:)
257 name:UISceneDidEnterBackgroundNotification
261 [center addObserver:self
262 selector:@selector(applicationWillEnterForeground:)
263 name:UIApplicationWillEnterForegroundNotification
265 [center addObserver:self
266 selector:@selector(applicationDidEnterBackground:)
267 name:UIApplicationDidEnterBackgroundNotification
271 - (void)recreatePlatformViewsController {
276 - (
flutter::IOSRenderingAPI)platformViewsRenderingAPI {
283 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
284 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
285 NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
286 [object detachFromEngineForRegistrar:registrar];
294 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
295 registrar.flutterEngine = nil;
301 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
302 if (_flutterViewControllerWillDeallocObserver) {
303 [center removeObserver:_flutterViewControllerWillDeallocObserver];
305 [center removeObserver:self];
313 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics {
314 if (!
self.platformView) {
317 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
320 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet {
321 if (!
self.platformView) {
324 self.platformView->DispatchPointerDataPacket(std::move(packet));
327 - (void)installFirstFrameCallback:(
void (^)(
void))block {
328 if (!
self.platformView) {
333 self.
platformView->SetNextFrameCallback([weakSelf, block] {
338 FML_DCHECK(strongSelf.platformTaskRunner);
339 FML_DCHECK(strongSelf.rasterTaskRunner);
340 FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
342 strongSelf.platformTaskRunner->PostTask([block]() { block(); });
346 - (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
347 if (!
self.platformView) {
351 self.platformView->SetAccessibilityFeatures(flags);
354 - (void)notifyViewCreated {
355 if (!
self.platformView) {
358 self.platformView->NotifyCreated();
361 - (void)notifyViewDestroyed {
362 if (!
self.platformView) {
365 self.platformView->NotifyDestroyed();
368 - (
flutter::PlatformViewIOS*)platformView {
375 - (
fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
379 return _shell->GetTaskRunners().GetPlatformTaskRunner();
382 - (
fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
386 return _shell->GetTaskRunners().GetUITaskRunner();
389 - (
fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
393 return _shell->GetTaskRunners().GetRasterTaskRunner();
396 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
397 callback:(FlutterKeyEventCallback)callback
398 userData:(
void*)userData API_AVAILABLE(ios(13.4)) {
399 if (@available(iOS 13.4, *)) {
403 if (!
self.platformView) {
406 const char* character =
event.character;
408 flutter::KeyData key_data;
410 key_data.timestamp = (uint64_t)event.timestamp;
411 switch (event.type) {
412 case kFlutterKeyEventTypeUp:
413 key_data.type = flutter::KeyEventType::kUp;
415 case kFlutterKeyEventTypeDown:
416 key_data.type = flutter::KeyEventType::kDown;
418 case kFlutterKeyEventTypeRepeat:
419 key_data.type = flutter::KeyEventType::kRepeat;
422 key_data.physical =
event.physical;
423 key_data.logical =
event.logical;
424 key_data.synthesized =
event.synthesized;
426 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
427 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
429 auto response = ^(NSData* reply) {
430 if (callback ==
nullptr) {
433 BOOL handled = FALSE;
434 if (reply.length == 1 && *
reinterpret_cast<const uint8_t*
>(reply.bytes) == 1) {
437 callback(handled, userData);
440 [
self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
443 - (void)ensureSemanticsEnabled {
444 if (!
self.platformView) {
447 self.platformView->SetSemanticsEnabled(
true);
451 FML_DCHECK(
self.platformView);
453 self.platformView->SetOwnerViewController(_viewController);
454 [
self maybeSetupPlatformViewChannels];
455 [
self updateDisplays];
460 self.flutterViewControllerWillDeallocObserver =
461 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
462 object:viewController
463 queue:[NSOperationQueue mainQueue]
464 usingBlock:^(NSNotification* note) {
465 [weakSelf notifyViewControllerDeallocated];
468 self.flutterViewControllerWillDeallocObserver = nil;
469 [
self notifyLowMemory];
474 FML_DCHECK(
self.platformView);
475 self.platformView->attachView();
478 - (void)setFlutterViewControllerWillDeallocObserver:(
id<NSObject>)observer {
479 if (observer != _flutterViewControllerWillDeallocObserver) {
480 if (_flutterViewControllerWillDeallocObserver) {
481 [[NSNotificationCenter defaultCenter]
482 removeObserver:_flutterViewControllerWillDeallocObserver];
484 _flutterViewControllerWillDeallocObserver = observer;
488 - (void)notifyViewControllerDeallocated {
489 [
self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
490 self.textInputPlugin.viewController = nil;
491 if (!
self.allowHeadlessExecution) {
492 [
self destroyContext];
493 }
else if (
self.platformView) {
494 self.platformView->SetOwnerViewController({});
496 [
self.textInputPlugin resetViewResponder];
497 _viewController = nil;
500 - (void)destroyContext {
501 [
self resetChannels];
502 self.isolateId = nil;
506 _platformViewsController = nil;
509 - (NSURL*)vmServiceUrl {
510 return self.publisher.url;
513 - (void)resetChannels {
514 self.localizationChannel = nil;
515 self.navigationChannel = nil;
516 self.restorationChannel = nil;
517 self.platformChannel = nil;
518 self.platformViewsChannel = nil;
519 self.textInputChannel = nil;
520 self.undoManagerChannel = nil;
521 self.scribbleChannel = nil;
522 self.lifecycleChannel = nil;
523 self.systemChannel = nil;
524 self.settingsChannel = nil;
525 self.keyEventChannel = nil;
526 self.spellCheckChannel = nil;
529 - (void)startProfiler {
530 FML_DCHECK(!_threadHost->name_prefix.empty());
531 _profiler = std::make_shared<flutter::SamplingProfiler>(
532 _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
534 flutter::ProfilerMetricsIOS profiler_metrics;
535 return profiler_metrics.GenerateSample();
544 - (void)setUpChannels {
548 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
549 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
558 binaryMessenger:self.binaryMessenger
561 self.navigationChannel =
563 binaryMessenger:self.binaryMessenger
566 if ([_initialRoute length] > 0) {
568 [
self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
572 self.restorationChannel =
574 binaryMessenger:self.binaryMessenger
577 self.platformChannel =
579 binaryMessenger:self.binaryMessenger
582 self.platformViewsChannel =
584 binaryMessenger:self.binaryMessenger
587 self.textInputChannel =
589 binaryMessenger:self.binaryMessenger
592 self.undoManagerChannel =
594 binaryMessenger:self.binaryMessenger
597 self.scribbleChannel =
599 binaryMessenger:self.binaryMessenger
602 self.spellCheckChannel =
604 binaryMessenger:self.binaryMessenger
607 self.lifecycleChannel =
609 binaryMessenger:self.binaryMessenger
614 binaryMessenger:self.binaryMessenger
617 self.settingsChannel =
619 binaryMessenger:self.binaryMessenger
622 self.keyEventChannel =
624 binaryMessenger:self.binaryMessenger
628 self.textInputPlugin.indirectScribbleDelegate =
self;
629 [
self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
634 self.restorationPlugin =
636 restorationEnabled:self.restorationEnabled];
639 self.screenshotChannel =
641 binaryMessenger:self.binaryMessenger
644 [
self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
645 FlutterResult _Nonnull result) {
647 if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
650 message:@"Requesting screenshot while engine is not running."
653 flutter::Rasterizer::Screenshot screenshot =
654 [strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
655 if (!screenshot.data) {
657 message:@"Unable to get screenshot."
661 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
662 length:screenshot.data->size()];
663 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
664 NSNumber* width = @(screenshot.frame_size.width);
665 NSNumber* height = @(screenshot.frame_size.height);
666 return result(@[ width, height, format ?: [NSNull null], data ]);
670 - (void)maybeSetupPlatformViewChannels {
671 if (
_shell &&
self.shell.IsSetup()) {
674 [
self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
675 [weakSelf.platformPlugin handleMethodCall:call result:result];
678 [
self.platformViewsChannel
679 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
681 [weakSelf.platformViewsController onMethodCall:call result:result];
685 [
self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
686 [weakSelf.textInputPlugin handleMethodCall:call result:result];
689 [
self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
690 [weakSelf.undoManagerPlugin handleMethodCall:call result:result];
693 [
self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
694 [weakSelf.spellCheckPlugin handleMethodCall:call result:result];
699 - (
flutter::Rasterizer::Screenshot)screenshot:(
flutter::Rasterizer::ScreenshotType)type
700 base64Encode:(
bool)base64Encode {
701 return self.shell.Screenshot(type, base64Encode);
704 - (void)launchEngine:(NSString*)entrypoint
705 libraryURI:(NSString*)libraryOrNil
706 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
708 flutter::RunConfiguration configuration =
709 [
self.dartProject runConfigurationForEntrypoint:entrypoint
710 libraryOrNil:libraryOrNil
711 entrypointArgs:entrypointArgs];
713 configuration.SetEngineId(
self.engineIdentifier);
714 self.shell.RunEngine(std::move(configuration));
717 - (void)setUpShell:(std::unique_ptr<
flutter::Shell>)shell
718 withVMServicePublication:(BOOL)doesVMServicePublication {
719 _shell = std::move(shell);
720 [
self setUpChannels];
721 [
self onLocaleUpdated:nil];
722 [
self updateDisplays];
724 initWithEnableVMServicePublication:doesVMServicePublication];
725 [
self maybeSetupPlatformViewChannels];
726 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
727 : flutter::GpuAvailability::kAvailable);
730 + (BOOL)isProfilerEnabled {
731 bool profilerEnabled =
false;
732 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
733 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
734 profilerEnabled =
true;
736 return profilerEnabled;
739 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
740 static size_t s_shellCount = 0;
741 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
744 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
745 const flutter::Settings& settings) {
748 fml::MessageLoop::EnsureInitializedForCurrentThread();
750 uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
751 if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) {
752 threadHostType |= flutter::ThreadHost::Type::kUi;
756 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
759 flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
762 host_config.ui_config =
763 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
764 flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
765 fml::Thread::ThreadPriority::kDisplay);
766 host_config.raster_config =
767 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
768 flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
769 fml::Thread::ThreadPriority::kRaster);
771 host_config.io_config =
772 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
773 flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
774 fml::Thread::ThreadPriority::kNormal);
776 return (flutter::ThreadHost){host_config};
779 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
781 FML_DCHECK(entrypoint) <<
"Must specify entrypoint if specifying library";
782 settings->advisory_script_entrypoint = entrypoint.UTF8String;
783 settings->advisory_script_uri = libraryURI.UTF8String;
784 }
else if (entrypoint) {
785 settings->advisory_script_entrypoint = entrypoint.UTF8String;
786 settings->advisory_script_uri = std::string(
"main.dart");
788 settings->advisory_script_entrypoint = std::string(
"main");
789 settings->advisory_script_uri = std::string(
"main.dart");
793 - (BOOL)createShell:(NSString*)entrypoint
794 libraryURI:(NSString*)libraryURI
795 initialRoute:(NSString*)initialRoute {
797 [FlutterLogger logWarning:@"This FlutterEngine was already invoked."];
801 self.initialRoute = initialRoute;
803 auto settings = [
self.dartProject settings];
804 if (initialRoute != nil) {
805 self.initialRoute = initialRoute;
806 }
else if (settings.route.empty() ==
false) {
807 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
810 auto platformData = [
self.dartProject defaultPlatformData];
812 SetEntryPoint(&settings, entrypoint, libraryURI);
814 NSString* threadLabel = [
FlutterEngine generateThreadLabel:self.labelPrefix];
815 _threadHost = std::make_shared<flutter::ThreadHost>();
816 *_threadHost = MakeThreadHost(threadLabel, settings);
819 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
820 [weakSelf](flutter::Shell& shell) {
823 return std::unique_ptr<flutter::PlatformViewIOS>();
825 [strongSelf recreatePlatformViewsController];
826 strongSelf.platformViewsController.taskRunner =
827 shell.GetTaskRunners().GetPlatformTaskRunner();
828 return std::make_unique<flutter::PlatformViewIOS>(
829 shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
830 shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
831 shell.GetIsGpuDisabledSyncSwitch());
834 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
835 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
837 fml::RefPtr<fml::TaskRunner> ui_runner;
838 if (settings.enable_impeller &&
839 settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) {
840 ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
842 ui_runner = _threadHost->ui_thread->GetTaskRunner();
844 flutter::TaskRunners task_runners(threadLabel.UTF8String,
845 fml::MessageLoop::GetCurrent().GetTaskRunner(),
846 _threadHost->raster_thread->GetTaskRunner(),
848 _threadHost->io_thread->GetTaskRunner()
852 self.isGpuDisabled =
self.viewController
853 ?
self.viewController.stateIsBackground
856 UIApplicationStateBackground;
859 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
863 on_create_platform_view,
864 on_create_rasterizer,
867 if (shell ==
nullptr) {
868 NSString* errorMessage = [NSString
869 stringWithFormat:@"Could not start a shell FlutterEngine with entrypoint: %@", entrypoint];
870 [FlutterLogger logError:errorMessage];
872 [
self setUpShell:std::move(shell)
873 withVMServicePublication:settings.enable_vm_service_publication];
875 [
self startProfiler];
882 - (void)updateDisplays {
887 auto vsync_waiter =
_shell->GetVsyncWaiter().lock();
888 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
889 std::vector<std::unique_ptr<flutter::Display>> displays;
890 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
891 auto scale = UIScreen.mainScreen.scale;
892 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
893 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
894 _shell->OnDisplayUpdates(std::move(displays));
898 return [
self runWithEntrypoint:FlutterDefaultDartEntrypoint
900 initialRoute:FlutterDefaultInitialRoute];
903 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
904 return [
self runWithEntrypoint:entrypoint
905 libraryURI:libraryURI
906 initialRoute:FlutterDefaultInitialRoute];
909 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
910 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
913 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
914 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
917 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
918 libraryURI:(NSString*)libraryURI
919 initialRoute:(NSString*)initialRoute {
920 return [
self runWithEntrypoint:entrypoint
921 libraryURI:libraryURI
922 initialRoute:initialRoute
926 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
927 libraryURI:(NSString*)libraryURI
928 initialRoute:(NSString*)initialRoute
929 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
930 if ([
self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
931 [
self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
937 - (void)notifyLowMemory {
939 _shell->NotifyLowMemoryWarning();
941 [
self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
944 #pragma mark - Text input delegate
947 updateEditingClient:(
int)client
948 withState:(NSDictionary*)state {
949 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
950 arguments:@[ @(client), state ]];
954 updateEditingClient:(
int)client
955 withState:(NSDictionary*)state
956 withTag:(NSString*)tag {
957 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
958 arguments:@[ @(client), @{tag : state} ]];
962 updateEditingClient:(
int)client
963 withDelta:(NSDictionary*)delta {
964 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
965 arguments:@[ @(client), delta ]];
969 updateFloatingCursor:(FlutterFloatingCursorDragState)state
970 withClient:(
int)client
971 withPosition:(NSDictionary*)position {
972 NSString* stateString;
974 case FlutterFloatingCursorDragStateStart:
975 stateString =
@"FloatingCursorDragState.start";
977 case FlutterFloatingCursorDragStateUpdate:
978 stateString =
@"FloatingCursorDragState.update";
980 case FlutterFloatingCursorDragStateEnd:
981 stateString =
@"FloatingCursorDragState.end";
984 [
self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
985 arguments:@[ @(client), stateString, position ]];
989 performAction:(FlutterTextInputAction)action
990 withClient:(
int)client {
991 NSString* actionString;
993 case FlutterTextInputActionUnspecified:
998 actionString =
@"TextInputAction.unspecified";
1000 case FlutterTextInputActionDone:
1001 actionString =
@"TextInputAction.done";
1003 case FlutterTextInputActionGo:
1004 actionString =
@"TextInputAction.go";
1006 case FlutterTextInputActionSend:
1007 actionString =
@"TextInputAction.send";
1009 case FlutterTextInputActionSearch:
1010 actionString =
@"TextInputAction.search";
1012 case FlutterTextInputActionNext:
1013 actionString =
@"TextInputAction.next";
1015 case FlutterTextInputActionContinue:
1016 actionString =
@"TextInputAction.continueAction";
1018 case FlutterTextInputActionJoin:
1019 actionString =
@"TextInputAction.join";
1021 case FlutterTextInputActionRoute:
1022 actionString =
@"TextInputAction.route";
1024 case FlutterTextInputActionEmergencyCall:
1025 actionString =
@"TextInputAction.emergencyCall";
1027 case FlutterTextInputActionNewline:
1028 actionString =
@"TextInputAction.newline";
1031 [
self.textInputChannel invokeMethod:@"TextInputClient.performAction"
1032 arguments:@[ @(client), actionString ]];
1036 showAutocorrectionPromptRectForStart:(NSUInteger)start
1038 withClient:(
int)client {
1039 [
self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1040 arguments:@[ @(client), @(start), @(end) ]];
1044 willDismissEditMenuWithTextInputClient:(
int)client {
1045 [
self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1046 arguments:@[ @(client) ]];
1050 shareSelectedText:(NSString*)selectedText {
1051 [
self.platformPlugin showShareViewController:selectedText];
1055 searchWebWithSelectedText:(NSString*)selectedText {
1056 [
self.platformPlugin searchWeb:selectedText];
1060 lookUpSelectedText:(NSString*)selectedText {
1061 [
self.platformPlugin showLookUpViewController:selectedText];
1065 performContextMenuCustomActionWithActionID:(NSString*)actionID
1066 textInputClient:(
int)client {
1067 [
self.platformChannel invokeMethod:@"ContextMenu.onPerformCustomAction"
1068 arguments:@[ @(client), actionID ]];
1071 #pragma mark - FlutterViewEngineDelegate
1077 [
self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1081 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1082 atPoint:(CGPoint)referencePoint
1087 [
self.textInputChannel
1088 invokeMethod:@"TextInputClient.focusElement"
1089 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1094 requestElementsInRect:(CGRect)rect
1099 [
self.textInputChannel
1100 invokeMethod:@"TextInputClient.requestElementsInRect"
1101 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1109 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1116 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
1120 insertTextPlaceholderWithSize:(CGSize)size
1121 withClient:(
int)client {
1125 [
self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
1126 arguments:@[ @(client), @(size.width), @(size.height) ]];
1130 removeTextPlaceholder:(
int)client {
1134 [
self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
1135 arguments:@[ @(client) ]];
1139 didResignFirstResponderWithTextInputClient:(
int)client {
1143 [
self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
1144 arguments:@[ @(client) ]];
1165 dispatch_async(dispatch_get_main_queue(), ^(
void) {
1166 long platform_view_id = [
self.platformViewsController firstResponderPlatformViewId];
1167 if (platform_view_id == -1) {
1171 [
self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1175 #pragma mark - Undo Manager Delegate
1177 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1178 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ?
@"undo" :
@"redo";
1179 [
self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1182 - (UIView<UITextInput>*)activeTextInputView {
1183 return [[
self textInputPlugin] textInputView];
1186 - (NSUndoManager*)undoManager {
1187 return self.viewController.undoManager;
1190 #pragma mark - Screenshot Delegate
1192 - (
flutter::Rasterizer::Screenshot)takeScreenshot:(
flutter::Rasterizer::ScreenshotType)type
1193 asBase64Encoded:(BOOL)base64Encode {
1194 FML_DCHECK(
_shell) <<
"Cannot takeScreenshot without a shell";
1195 return _shell->Screenshot(type, base64Encode);
1198 - (void)flutterViewAccessibilityDidCall {
1200 [
self ensureSemanticsEnabled];
1222 #pragma mark - FlutterBinaryMessenger
1224 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1225 [
self sendOnChannel:channel message:message binaryReply:nil];
1228 - (void)sendOnChannel:(NSString*)channel
1229 message:(NSData*)message
1231 NSParameterAssert(channel);
1233 @"Sending a message before the FlutterEngine has been run.");
1234 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1235 (callback == nil) ?
nullptr
1236 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1240 _shell->GetTaskRunners().GetPlatformTaskRunner());
1241 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1242 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1243 : std::make_unique<flutter::PlatformMessage>(
1246 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1256 binaryMessageHandler:
1258 return [
self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1262 setMessageHandlerOnChannel:(NSString*)channel
1265 NSParameterAssert(channel);
1267 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1268 handler, taskQueue);
1269 return [
self.connections acquireConnectionForChannel:channel];
1271 NSAssert(!handler,
@"Setting a message handler before the FlutterEngine has been run.");
1273 return [FlutterConnectionCollection makeErrorConnectionWithErrorCode:-1L];
1279 NSString* channel = [
self.connections cleanupConnectionWithID:connection];
1280 if (channel.length > 0) {
1281 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String, nil,
1287 #pragma mark - FlutterTextureRegistry
1290 FML_DCHECK(
self.platformView);
1291 int64_t textureId =
self.nextTextureId++;
1292 self.platformView->RegisterExternalTexture(textureId, texture);
1296 - (void)unregisterTexture:(int64_t)textureId {
1297 _shell->GetPlatformView()->UnregisterTexture(textureId);
1300 - (void)textureFrameAvailable:(int64_t)textureId {
1301 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1304 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1308 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1312 - (id<FlutterPluginRegistry>)pluginRegistry {
1316 #pragma mark - FlutterPluginRegistry
1319 NSAssert(
self.pluginPublications[pluginKey] == nil,
@"Duplicate plugin key: %@", pluginKey);
1320 self.pluginPublications[pluginKey] = [NSNull null];
1322 flutterEngine:self];
1323 self.registrars[pluginKey] = result;
1327 - (BOOL)hasPlugin:(NSString*)pluginKey {
1328 return _pluginPublications[pluginKey] != nil;
1331 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1332 return _pluginPublications[pluginKey];
1335 #pragma mark - Notifications
1337 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1338 [
self flutterWillEnterForeground:notification];
1341 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1342 [
self flutterDidEnterBackground:notification];
1345 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1346 [
self flutterWillEnterForeground:notification];
1349 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1350 [
self flutterDidEnterBackground:notification];
1353 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1354 [
self setIsGpuDisabled:NO];
1357 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1358 [
self setIsGpuDisabled:YES];
1359 [
self notifyLowMemory];
1362 - (void)onMemoryWarning:(NSNotification*)notification {
1363 [
self notifyLowMemory];
1366 - (void)setIsGpuDisabled:(BOOL)value {
1368 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1369 : flutter::GpuAvailability::kAvailable);
1371 _isGpuDisabled = value;
1374 #pragma mark - Locale updates
1376 - (void)onLocaleUpdated:(NSNotification*)notification {
1378 NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
1379 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1380 for (NSString* localeID in preferredLocales) {
1381 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1382 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1383 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1384 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1385 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1386 if (!languageCode) {
1389 [localeData addObject:languageCode];
1390 [localeData addObject:(countryCode ? countryCode : @"")];
1391 [localeData addObject:(scriptCode ? scriptCode : @"")];
1392 [localeData addObject:(variantCode ? variantCode : @"")];
1394 if (localeData.count == 0) {
1397 [
self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1400 - (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1401 callback:(NS_NOESCAPE
void (^_Nonnull)(BOOL didTimeout))callback {
1402 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1403 fml::Status status =
self.shell.WaitForFirstFrame(waitTime);
1404 callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1407 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1408 callback:(
void (^_Nonnull)(BOOL didTimeout))callback {
1409 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1410 dispatch_group_t group = dispatch_group_create();
1413 __block BOOL didTimeout = NO;
1414 dispatch_group_async(group, queue, ^{
1420 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1421 fml::Status status = strongSelf.
shell.WaitForFirstFrame(waitTime);
1422 didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
1426 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1440 callback(didTimeout);
1444 - (
FlutterEngine*)spawnWithEntrypoint:( NSString*)entrypoint
1445 libraryURI:( NSString*)libraryURI
1446 initialRoute:( NSString*)initialRoute
1447 entrypointArgs:( NSArray<NSString*>*)entrypointArgs {
1448 NSAssert(
_shell,
@"Spawning from an engine without a shell (possibly not run).");
1450 project:self.dartProject
1451 allowHeadlessExecution:self.allowHeadlessExecution];
1452 flutter::RunConfiguration configuration =
1453 [
self.dartProject runConfigurationForEntrypoint:entrypoint
1454 libraryOrNil:libraryURI
1455 entrypointArgs:entrypointArgs];
1457 configuration.SetEngineId(result.engineIdentifier);
1464 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->
GetIosContext();
1465 FML_DCHECK(context);
1469 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1470 [result, context](flutter::Shell& shell) {
1471 [result recreatePlatformViewsController];
1472 result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
1473 return std::make_unique<flutter::PlatformViewIOS>(
1474 shell, context, result.platformViewsController, shell.GetTaskRunners());
1477 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1478 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
1480 std::string cppInitialRoute;
1482 cppInitialRoute = [initialRoute UTF8String];
1485 std::unique_ptr<flutter::Shell> shell =
_shell->Spawn(
1486 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1488 result->_threadHost = _threadHost;
1490 result->_isGpuDisabled = _isGpuDisabled;
1491 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1495 - (const
flutter::ThreadHost&)threadHost {
1496 return *_threadHost;
1500 return self.dartProject;
1506 NSString* _pluginKey;
1509 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
1510 self = [
super init];
1511 NSAssert(
self,
@"Super init cannot be nil");
1512 _pluginKey = [pluginKey copy];
1513 _flutterEngine = flutterEngine;
1518 return _flutterEngine.binaryMessenger;
1522 return _flutterEngine.textureRegistry;
1526 return _flutterEngine.viewController;
1529 - (void)publish:(NSObject*)value {
1530 _flutterEngine.pluginPublications[_pluginKey] = value;
1533 - (void)addMethodCallDelegate:(NSObject<
FlutterPlugin>*)delegate
1540 - (void)addApplicationDelegate:(NSObject<
FlutterPlugin>*)delegate
1541 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1542 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1544 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1545 (id<FlutterAppLifeCycleProvider>)appDelegate;
1546 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1550 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1551 return [_flutterEngine lookupKeyForAsset:asset];
1554 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1555 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1559 withId:(NSString*)factoryId {
1560 [
self registerViewFactory:factory
1562 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1566 withId:(NSString*)factoryId
1567 gestureRecognizersBlockingPolicy:
1569 [_flutterEngine.platformViewsController registerViewFactory:factory
1571 gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
int64_t FlutterBinaryMessengerConnection
void(^ FlutterResult)(id _Nullable result)
NSString *const FlutterDefaultDartEntrypoint
std::shared_ptr< flutter::SamplingProfiler > _profiler
std::unique_ptr< flutter::Shell > _shell
NSString *const kFlutterKeyDataChannel
NSString *const FlutterDefaultInitialRoute
flutter::IOSRenderingAPI _renderingApi
FlutterTextureRegistryRelay * _textureRegistry
static FLUTTER_ASSERT_ARC void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig &config)
static constexpr int kNumProfilerSamplesPerSec
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterPlatformViewGestureRecognizersBlockingPolicy
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
FlutterEngineProcTable & embedderAPI
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
const flutter::Settings & settings()
NSString * lookupKeyForAsset:(NSString *asset)
NSObject< FlutterBinaryMessenger > * parent
FlutterMethodChannel * textInputChannel
flutter::PlatformViewIOS * platformView()
FlutterMethodChannel * navigationChannel
FlutterBasicMessageChannel * keyEventChannel
FlutterBasicMessageChannel * lifecycleChannel
FlutterMethodChannel * platformChannel
FlutterMethodChannel * localizationChannel
FlutterBasicMessageChannel * systemChannel
FlutterBasicMessageChannel * settingsChannel
FlutterMethodChannel * restorationChannel
FlutterEngine * flutterEngine
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
UIApplication * application
NSObject< FlutterTextureRegistry > * parent
fml::MallocMapping CopyNSDataToMapping(NSData *data)
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
instancetype sharedInstance()
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)