Flutter Engine
FlutterEngine.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define FML_USED_ON_EMBEDDER
6 
7 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
8 
9 #include <memory>
10 
11 #include "flutter/fml/message_loop.h"
12 #include "flutter/fml/platform/darwin/platform_version.h"
13 #include "flutter/fml/trace_event.h"
14 #include "flutter/runtime/ptrace_check.h"
15 #include "flutter/shell/common/engine.h"
16 #include "flutter/shell/common/platform_view.h"
17 #include "flutter/shell/common/shell.h"
18 #include "flutter/shell/common/switches.h"
19 #include "flutter/shell/common/thread_host.h"
20 #include "flutter/shell/common/variable_refresh_rate_display.h"
21 #import "flutter/shell/platform/darwin/common/command_line.h"
22 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h"
23 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
24 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterIndirectScribbleDelegate.h"
25 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h"
26 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
27 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
28 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h"
29 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h"
30 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
31 #import "flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h"
32 #import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
33 #import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
34 #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
35 #import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
36 #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
37 #include "flutter/shell/profiling/sampling_profiler.h"
38 
39 /// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities
40 /// Using iOS platform thread API to configure thread priority
42  // set thread name
44 
45  // set thread priority
46  switch (config.priority) {
48  [[NSThread currentThread] setThreadPriority:0];
49  break;
50  }
52  [[NSThread currentThread] setThreadPriority:0.5];
53  break;
54  }
57  [[NSThread currentThread] setThreadPriority:1.0];
58  sched_param param;
59  int policy;
60  pthread_t thread = pthread_self();
61  if (!pthread_getschedparam(thread, &policy, &param)) {
62  param.sched_priority = 50;
63  pthread_setschedparam(thread, policy, &param);
64  }
65  break;
66  }
67  }
68 }
69 
70 #pragma mark - Public exported constants
71 
72 NSString* const FlutterDefaultDartEntrypoint = nil;
73 NSString* const FlutterDefaultInitialRoute = nil;
74 
75 #pragma mark - Internal constants
76 
77 NSString* const kFlutterEngineWillDealloc = @"FlutterEngineWillDealloc";
78 NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
79 static constexpr int kNumProfilerSamplesPerSec = 5;
80 
81 @interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
82 @property(nonatomic, assign) FlutterEngine* flutterEngine;
83 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine;
84 @end
85 
86 @interface FlutterEngine () <FlutterIndirectScribbleDelegate,
87  FlutterUndoManagerDelegate,
88  FlutterTextInputDelegate,
89  FlutterBinaryMessenger>
90 // Maintains a dictionary of plugin names that have registered with the engine. Used by
91 // FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
92 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
93 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
94 
95 @property(nonatomic, readwrite, copy) NSString* isolateId;
96 @property(nonatomic, copy) NSString* initialRoute;
97 @property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
98 @end
99 
100 @implementation FlutterEngine {
102  std::shared_ptr<flutter::ThreadHost> _threadHost;
103  std::unique_ptr<flutter::Shell> _shell;
104  NSString* _labelPrefix;
105  std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;
106 
109 
110  std::shared_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
112  std::shared_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
113  std::shared_ptr<flutter::SamplingProfiler> _profiler;
114 
115  // Channels
131 
132  int64_t _nextTextureId;
133 
137  std::unique_ptr<flutter::ConnectionCollection> _connections;
138 }
139 
140 - (instancetype)init {
141  return [self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
142 }
143 
144 - (instancetype)initWithName:(NSString*)labelPrefix {
145  return [self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
146 }
147 
148 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
149  return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
150 }
151 
152 - (instancetype)initWithName:(NSString*)labelPrefix
153  project:(FlutterDartProject*)project
154  allowHeadlessExecution:(BOOL)allowHeadlessExecution {
155  return [self initWithName:labelPrefix
156  project:project
157  allowHeadlessExecution:allowHeadlessExecution
158  restorationEnabled:NO];
159 }
160 
161 - (instancetype)initWithName:(NSString*)labelPrefix
162  project:(FlutterDartProject*)project
163  allowHeadlessExecution:(BOOL)allowHeadlessExecution
164  restorationEnabled:(BOOL)restorationEnabled {
165  self = [super init];
166  NSAssert(self, @"Super init cannot be nil");
167  NSAssert(labelPrefix, @"labelPrefix is required");
168 
169  _restorationEnabled = restorationEnabled;
170  _allowHeadlessExecution = allowHeadlessExecution;
171  _labelPrefix = [labelPrefix copy];
172 
173  _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
174 
175  if (project == nil) {
176  _dartProject.reset([[FlutterDartProject alloc] init]);
177  } else {
178  _dartProject.reset([project retain]);
179  }
180 
181  if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
182  NSLog(
183  @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
184  @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
185  @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
186  @"profile and release mode apps can be launched from the home screen.");
187  [self release];
188  return nil;
189  }
190 
191  _pluginPublications = [[NSMutableDictionary alloc] init];
192  _registrars = [[NSMutableDictionary alloc] init];
193  [self recreatePlatformViewController];
194 
195  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
197 
198  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
199  [center addObserver:self
200  selector:@selector(onMemoryWarning:)
201  name:UIApplicationDidReceiveMemoryWarningNotification
202  object:nil];
203 
204  [center addObserver:self
205  selector:@selector(applicationWillEnterForeground:)
206  name:UIApplicationWillEnterForegroundNotification
207  object:nil];
208 
209  [center addObserver:self
210  selector:@selector(applicationDidEnterBackground:)
211  name:UIApplicationDidEnterBackgroundNotification
212  object:nil];
213 
214  [center addObserver:self
215  selector:@selector(onLocaleUpdated:)
216  name:NSCurrentLocaleDidChangeNotification
217  object:nil];
218 
219  return self;
220 }
221 
222 - (void)recreatePlatformViewController {
225 }
226 
227 - (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
228  return _renderingApi;
229 }
230 
231 - (void)dealloc {
232  /// Notify plugins of dealloc. This should happen first in dealloc since the
233  /// plugins may be talking to things like the binaryMessenger.
234  [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
235  if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
236  NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
237  [object detachFromEngineForRegistrar:registrar];
238  }
239  }];
240 
241  [[NSNotificationCenter defaultCenter] postNotificationName:kFlutterEngineWillDealloc
242  object:self
243  userInfo:nil];
244 
245  // It will be destroyed and invalidate its weak pointers
246  // before any other members are destroyed.
247  _weakFactory.reset();
248 
249  /// nil out weak references.
250  [_registrars
251  enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
252  registrar.flutterEngine = nil;
253  }];
254 
255  [_labelPrefix release];
256  [_initialRoute release];
257  [_pluginPublications release];
258  [_registrars release];
259  _binaryMessenger.parent = nil;
260  [_binaryMessenger release];
261  [_isolateId release];
262 
263  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
264  if (_flutterViewControllerWillDeallocObserver) {
265  [center removeObserver:_flutterViewControllerWillDeallocObserver];
266  [_flutterViewControllerWillDeallocObserver release];
267  }
268  [center removeObserver:self];
269 
270  [super dealloc];
271 }
272 
273 - (flutter::Shell&)shell {
275  return *_shell;
276 }
277 
278 - (fml::WeakPtr<FlutterEngine>)getWeakPtr {
279  return _weakFactory->GetWeakPtr();
280 }
281 
282 - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
283  if (!self.platformView) {
284  return;
285  }
286  self.platformView->SetViewportMetrics(std::move(viewportMetrics));
287 }
288 
289 - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
290  if (!self.platformView) {
291  return;
292  }
293  self.platformView->DispatchPointerDataPacket(std::move(packet));
294 }
295 
296 - (fml::WeakPtr<flutter::PlatformView>)platformView {
298  return _shell->GetPlatformView();
299 }
300 
301 - (flutter::PlatformViewIOS*)iosPlatformView {
303  return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get());
304 }
305 
306 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
308  return _shell->GetTaskRunners().GetPlatformTaskRunner();
309 }
310 
311 - (fml::RefPtr<fml::TaskRunner>)RasterTaskRunner {
313  return _shell->GetTaskRunners().GetRasterTaskRunner();
314 }
315 
316 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
318  userData:(void*)userData API_AVAILABLE(ios(13.4)) {
319  if (@available(iOS 13.4, *)) {
320  } else {
321  return;
322  }
323  if (!self.platformView) {
324  return;
325  }
326  const char* character = event.character;
327 
328  flutter::KeyData key_data;
329  key_data.Clear();
330  key_data.timestamp = (uint64_t)event.timestamp;
331  switch (event.type) {
333  key_data.type = flutter::KeyEventType::kUp;
334  break;
337  break;
340  break;
341  }
342  key_data.physical = event.physical;
343  key_data.logical = event.logical;
344  key_data.synthesized = event.synthesized;
345 
346  auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
347  NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
348 
349  auto response = ^(NSData* reply) {
350  if (callback == nullptr) {
351  return;
352  }
353  BOOL handled = FALSE;
354  if (reply.length == 1 && *reinterpret_cast<const uint8_t*>(reply.bytes) == 1) {
355  handled = TRUE;
356  }
357  callback(handled, userData);
358  };
359 
360  [self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
361 }
362 
363 - (void)ensureSemanticsEnabled {
364  self.iosPlatformView->SetSemanticsEnabled(true);
365 }
366 
367 - (void)setViewController:(FlutterViewController*)viewController {
368  FML_DCHECK(self.iosPlatformView);
371  self.iosPlatformView->SetOwnerViewController(_viewController);
372  [self maybeSetupPlatformViewChannels];
373  _textInputPlugin.get().viewController = viewController;
374  _undoManagerPlugin.get().viewController = viewController;
375 
376  if (viewController) {
377  __block FlutterEngine* blockSelf = self;
378  self.flutterViewControllerWillDeallocObserver =
379  [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
380  object:viewController
381  queue:[NSOperationQueue mainQueue]
382  usingBlock:^(NSNotification* note) {
383  [blockSelf notifyViewControllerDeallocated];
384  }];
385  } else {
386  self.flutterViewControllerWillDeallocObserver = nil;
387  [self notifyLowMemory];
388  }
389 }
390 
391 - (void)attachView {
392  self.iosPlatformView->attachView();
393  [_textInputPlugin.get() setupIndirectScribbleInteraction:self.viewController];
394 }
395 
396 - (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
397  if (observer != _flutterViewControllerWillDeallocObserver) {
398  if (_flutterViewControllerWillDeallocObserver) {
399  [[NSNotificationCenter defaultCenter]
400  removeObserver:_flutterViewControllerWillDeallocObserver];
401  [_flutterViewControllerWillDeallocObserver release];
402  }
403  _flutterViewControllerWillDeallocObserver = [observer retain];
404  }
405 }
406 
407 - (void)notifyViewControllerDeallocated {
408  [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
409  _textInputPlugin.get().viewController = nil;
410  _undoManagerPlugin.get().viewController = nil;
412  [self destroyContext];
413  } else {
414  flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
415  if (platform_view) {
416  platform_view->SetOwnerViewController({});
417  }
418  }
419  [_textInputPlugin.get() resetViewResponder];
420  _viewController.reset();
421 }
422 
423 - (void)destroyContext {
424  [self resetChannels];
425  self.isolateId = nil;
426  _shell.reset();
427  _profiler.reset();
428  _threadHost.reset();
429  _platformViewsController.reset();
430 }
431 
433  if (!_viewController) {
434  return nil;
435  }
436  return _viewController.get();
437 }
438 
439 - (FlutterPlatformPlugin*)platformPlugin {
440  return _platformPlugin.get();
441 }
442 - (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController {
444 }
446  return _textInputPlugin.get();
447 }
448 - (FlutterUndoManagerPlugin*)undoManagerPlugin {
449  return _undoManagerPlugin.get();
450 }
451 - (FlutterRestorationPlugin*)restorationPlugin {
452  return _restorationPlugin.get();
453 }
454 - (FlutterMethodChannel*)localizationChannel {
455  return _localizationChannel.get();
456 }
457 - (FlutterMethodChannel*)navigationChannel {
458  return _navigationChannel.get();
459 }
460 - (FlutterMethodChannel*)restorationChannel {
461  return _restorationChannel.get();
462 }
463 - (FlutterMethodChannel*)platformChannel {
464  return _platformChannel.get();
465 }
466 - (FlutterMethodChannel*)textInputChannel {
467  return _textInputChannel.get();
468 }
469 - (FlutterMethodChannel*)undoManagerChannel {
470  return _undoManagerChannel.get();
471 }
472 - (FlutterBasicMessageChannel*)lifecycleChannel {
473  return _lifecycleChannel.get();
474 }
475 - (FlutterBasicMessageChannel*)systemChannel {
476  return _systemChannel.get();
477 }
478 - (FlutterBasicMessageChannel*)settingsChannel {
479  return _settingsChannel.get();
480 }
481 - (FlutterBasicMessageChannel*)keyEventChannel {
482  return _keyEventChannel.get();
483 }
484 
485 - (NSURL*)observatoryUrl {
486  return [_publisher.get() url];
487 }
488 
489 - (void)resetChannels {
497  _lifecycleChannel.reset();
498  _systemChannel.reset();
499  _settingsChannel.reset();
500  _keyEventChannel.reset();
501 }
502 
503 - (void)startProfiler {
504  FML_DCHECK(!_threadHost->name_prefix.empty());
505  _profiler_metrics = std::make_shared<flutter::ProfilerMetricsIOS>();
506  _profiler = std::make_shared<flutter::SamplingProfiler>(
507  _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
508  [self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
509  _profiler->Start();
510 }
511 
512 // If you add a channel, be sure to also update `resetChannels`.
513 // Channels get a reference to the engine, and therefore need manual
514 // cleanup for proper collection.
515 - (void)setupChannels {
516  // This will be invoked once the shell is done setting up and the isolate ID
517  // for the UI isolate is available.
518  fml::WeakPtr<FlutterEngine> weakSelf = [self getWeakPtr];
519  [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
520  binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
521  if (weakSelf) {
522  weakSelf.get().isolateId =
523  [[FlutterStringCodec sharedInstance] decode:message];
524  }
525  }];
526 
528  initWithName:@"flutter/localization"
529  binaryMessenger:self.binaryMessenger
530  codec:[FlutterJSONMethodCodec sharedInstance]]);
531 
533  initWithName:@"flutter/navigation"
534  binaryMessenger:self.binaryMessenger
535  codec:[FlutterJSONMethodCodec sharedInstance]]);
536 
537  if ([_initialRoute length] > 0) {
538  // Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
539  [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
540  [_initialRoute release];
541  _initialRoute = nil;
542  }
543 
545  initWithName:@"flutter/restoration"
546  binaryMessenger:self.binaryMessenger
547  codec:[FlutterStandardMethodCodec sharedInstance]]);
548 
550  initWithName:@"flutter/platform"
551  binaryMessenger:self.binaryMessenger
552  codec:[FlutterJSONMethodCodec sharedInstance]]);
553 
555  initWithName:@"flutter/platform_views"
556  binaryMessenger:self.binaryMessenger
557  codec:[FlutterStandardMethodCodec sharedInstance]]);
558 
560  initWithName:@"flutter/textinput"
561  binaryMessenger:self.binaryMessenger
562  codec:[FlutterJSONMethodCodec sharedInstance]]);
563 
565  initWithName:@"flutter/undomanager"
566  binaryMessenger:self.binaryMessenger
567  codec:[FlutterJSONMethodCodec sharedInstance]]);
568 
570  initWithName:@"flutter/lifecycle"
571  binaryMessenger:self.binaryMessenger
572  codec:[FlutterStringCodec sharedInstance]]);
573 
575  initWithName:@"flutter/system"
576  binaryMessenger:self.binaryMessenger
577  codec:[FlutterJSONMessageCodec sharedInstance]]);
578 
580  initWithName:@"flutter/settings"
581  binaryMessenger:self.binaryMessenger
582  codec:[FlutterJSONMessageCodec sharedInstance]]);
583 
585  initWithName:@"flutter/keyevent"
586  binaryMessenger:self.binaryMessenger
587  codec:[FlutterJSONMessageCodec sharedInstance]]);
588 
589  FlutterTextInputPlugin* textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
590  _textInputPlugin.reset(textInputPlugin);
591  textInputPlugin.indirectScribbleDelegate = self;
592  [textInputPlugin setupIndirectScribbleInteraction:self.viewController];
593 
594  FlutterUndoManagerPlugin* undoManagerPlugin =
595  [[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
596  _undoManagerPlugin.reset(undoManagerPlugin);
597 
598  _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]);
599 
601  initWithChannel:_restorationChannel.get()
602  restorationEnabled:_restorationEnabled]);
603 }
604 
605 - (void)maybeSetupPlatformViewChannels {
606  if (_shell && self.shell.IsSetup()) {
607  FlutterPlatformPlugin* platformPlugin = _platformPlugin.get();
608  [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
609  [platformPlugin handleMethodCall:call result:result];
610  }];
611 
612  fml::WeakPtr<FlutterEngine> weakSelf = [self getWeakPtr];
613  [_platformViewsChannel.get()
614  setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
615  if (weakSelf) {
616  weakSelf.get().platformViewsController->OnMethodCall(call, result);
617  }
618  }];
619 
621  [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
622  [textInputPlugin handleMethodCall:call result:result];
623  }];
624 
625  FlutterUndoManagerPlugin* undoManagerPlugin = _undoManagerPlugin.get();
626  [_undoManagerChannel.get()
627  setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
628  [undoManagerPlugin handleMethodCall:call result:result];
629  }];
630  }
631 }
632 
634  base64Encode:(bool)base64Encode {
635  return self.shell.Screenshot(type, base64Encode);
636 }
637 
638 - (void)launchEngine:(NSString*)entrypoint
639  libraryURI:(NSString*)libraryOrNil
640  entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
641  // Launch the Dart application with the inferred run configuration.
642  self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
643  libraryOrNil:libraryOrNil
644  entrypointArgs:entrypointArgs]);
645 }
646 
647 - (void)setupShell:(std::unique_ptr<flutter::Shell>)shell
648  withObservatoryPublication:(BOOL)doesObservatoryPublication {
649  _shell = std::move(shell);
650  [self setupChannels];
651  [self onLocaleUpdated:nil];
652  [self initializeDisplays];
654  initWithEnableObservatoryPublication:doesObservatoryPublication]);
655  [self maybeSetupPlatformViewChannels];
656  _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
658 }
659 
660 + (BOOL)isProfilerEnabled {
661  bool profilerEnabled = false;
662 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
663  (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
664  profilerEnabled = true;
665 #endif
666  return profilerEnabled;
667 }
668 
669 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
670  static size_t s_shellCount = 0;
671  return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
672 }
673 
674 + (flutter::ThreadHost)makeThreadHost:(NSString*)threadLabel {
675  // The current thread will be used as the platform thread. Ensure that the message loop is
676  // initialized.
678 
679  uint32_t threadHostType = flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::RASTER |
680  flutter::ThreadHost::Type::IO;
681 
682  if ([FlutterEngine isProfilerEnabled]) {
683  threadHostType = threadHostType | flutter::ThreadHost::Type::Profiler;
684  }
685 
686  flutter::ThreadHost::ThreadHostConfig host_config(threadLabel.UTF8String, threadHostType,
688 
689  host_config.ui_config =
691  flutter::ThreadHost::Type::UI, threadLabel.UTF8String),
693 
694  host_config.raster_config =
696  flutter::ThreadHost::Type::RASTER, threadLabel.UTF8String),
698 
699  host_config.io_config =
701  flutter::ThreadHost::Type::IO, threadLabel.UTF8String),
703 
704  return (flutter::ThreadHost){host_config};
705 }
706 
707 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
708  if (libraryURI) {
709  FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library";
710  settings->advisory_script_entrypoint = entrypoint.UTF8String;
711  settings->advisory_script_uri = libraryURI.UTF8String;
712  } else if (entrypoint) {
713  settings->advisory_script_entrypoint = entrypoint.UTF8String;
714  settings->advisory_script_uri = std::string("main.dart");
715  } else {
716  settings->advisory_script_entrypoint = std::string("main");
717  settings->advisory_script_uri = std::string("main.dart");
718  }
719 }
720 
721 - (BOOL)createShell:(NSString*)entrypoint
722  libraryURI:(NSString*)libraryURI
723  initialRoute:(NSString*)initialRoute {
724  if (_shell != nullptr) {
725  FML_LOG(WARNING) << "This FlutterEngine was already invoked.";
726  return NO;
727  }
728 
729  self.initialRoute = initialRoute;
730 
731  auto settings = [_dartProject.get() settings];
732  if (initialRoute != nil) {
733  self.initialRoute = initialRoute;
734  } else if (settings.route.empty() == false) {
735  self.initialRoute = [NSString stringWithCString:settings.route.c_str()
736  encoding:NSUTF8StringEncoding];
737  }
738 
740 
741  auto platformData = [_dartProject.get() defaultPlatformData];
742 
743  SetEntryPoint(&settings, entrypoint, libraryURI);
744 
745  NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix];
746  _threadHost = std::make_shared<flutter::ThreadHost>();
747  *_threadHost = [FlutterEngine makeThreadHost:threadLabel];
748 
749  // Lambda captures by pointers to ObjC objects are fine here because the
750  // create call is synchronous.
752  [self](flutter::Shell& shell) {
753  [self recreatePlatformViewController];
754  return std::make_unique<flutter::PlatformViewIOS>(
755  shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners());
756  };
757 
759  [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
760 
761  flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
763  _threadHost->raster_thread->GetTaskRunner(), // raster
764  _threadHost->ui_thread->GetTaskRunner(), // ui
765  _threadHost->io_thread->GetTaskRunner() // io
766  );
767 
768  _isGpuDisabled =
769  [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
770  // Create the shell. This is a blocking operation.
771  std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
772  /*platform_data=*/std::move(platformData),
773  /*task_runners=*/std::move(task_runners),
774  /*settings=*/std::move(settings),
775  /*on_create_platform_view=*/on_create_platform_view,
776  /*on_create_rasterizer=*/on_create_rasterizer,
777  /*is_gpu_disabled=*/_isGpuDisabled);
778 
779  if (shell == nullptr) {
780  FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: "
781  << entrypoint.UTF8String;
782  } else {
783  [self setupShell:std::move(shell)
784  withObservatoryPublication:settings.enable_observatory_publication];
785  if ([FlutterEngine isProfilerEnabled]) {
786  [self startProfiler];
787  }
788  }
789 
790  return _shell != nullptr;
791 }
792 
793 - (void)initializeDisplays {
794  auto vsync_waiter = std::shared_ptr<flutter::VsyncWaiter>(_shell->GetVsyncWaiter().lock());
795  auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
796  std::vector<std::unique_ptr<flutter::Display>> displays;
797  displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(vsync_waiter_ios));
798  _shell->OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, std::move(displays));
799 }
800 
801 - (BOOL)run {
802  return [self runWithEntrypoint:FlutterDefaultDartEntrypoint
803  libraryURI:nil
804  initialRoute:FlutterDefaultInitialRoute];
805 }
806 
807 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
808  return [self runWithEntrypoint:entrypoint
809  libraryURI:libraryURI
810  initialRoute:FlutterDefaultInitialRoute];
811 }
812 
813 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
814  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
815 }
816 
817 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
818  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
819 }
820 
821 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
822  libraryURI:(NSString*)libraryURI
823  initialRoute:(NSString*)initialRoute {
824  return [self runWithEntrypoint:entrypoint
825  libraryURI:libraryURI
826  initialRoute:initialRoute
827  entrypointArgs:nil];
828 }
829 
830 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
831  libraryURI:(NSString*)libraryURI
832  initialRoute:(NSString*)initialRoute
833  entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
834  if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
835  [self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
836  }
837 
838  return _shell != nullptr;
839 }
840 
841 - (void)notifyLowMemory {
842  if (_shell) {
843  _shell->NotifyLowMemoryWarning();
844  }
845  [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
846 }
847 
848 #pragma mark - Text input delegate
849 
850 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
851  updateEditingClient:(int)client
852  withState:(NSDictionary*)state {
853  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
854  arguments:@[ @(client), state ]];
855 }
856 
857 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
858  updateEditingClient:(int)client
859  withState:(NSDictionary*)state
860  withTag:(NSString*)tag {
861  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
862  arguments:@[ @(client), @{tag : state} ]];
863 }
864 
865 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
866  updateEditingClient:(int)client
867  withDelta:(NSDictionary*)delta {
868  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
869  arguments:@[ @(client), delta ]];
870 }
871 
872 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
873  updateFloatingCursor:(FlutterFloatingCursorDragState)state
874  withClient:(int)client
875  withPosition:(NSDictionary*)position {
876  NSString* stateString;
877  switch (state) {
878  case FlutterFloatingCursorDragStateStart:
879  stateString = @"FloatingCursorDragState.start";
880  break;
881  case FlutterFloatingCursorDragStateUpdate:
882  stateString = @"FloatingCursorDragState.update";
883  break;
884  case FlutterFloatingCursorDragStateEnd:
885  stateString = @"FloatingCursorDragState.end";
886  break;
887  }
888  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
889  arguments:@[ @(client), stateString, position ]];
890 }
891 
892 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
893  performAction:(FlutterTextInputAction)action
894  withClient:(int)client {
895  NSString* actionString;
896  switch (action) {
897  case FlutterTextInputActionUnspecified:
898  // Where did the term "unspecified" come from? iOS has a "default" and Android
899  // has "unspecified." These 2 terms seem to mean the same thing but we need
900  // to pick just one. "unspecified" was chosen because "default" is often a
901  // reserved word in languages with switch statements (dart, java, etc).
902  actionString = @"TextInputAction.unspecified";
903  break;
904  case FlutterTextInputActionDone:
905  actionString = @"TextInputAction.done";
906  break;
907  case FlutterTextInputActionGo:
908  actionString = @"TextInputAction.go";
909  break;
910  case FlutterTextInputActionSend:
911  actionString = @"TextInputAction.send";
912  break;
913  case FlutterTextInputActionSearch:
914  actionString = @"TextInputAction.search";
915  break;
916  case FlutterTextInputActionNext:
917  actionString = @"TextInputAction.next";
918  break;
919  case FlutterTextInputActionContinue:
920  actionString = @"TextInputAction.continue";
921  break;
922  case FlutterTextInputActionJoin:
923  actionString = @"TextInputAction.join";
924  break;
925  case FlutterTextInputActionRoute:
926  actionString = @"TextInputAction.route";
927  break;
928  case FlutterTextInputActionEmergencyCall:
929  actionString = @"TextInputAction.emergencyCall";
930  break;
931  case FlutterTextInputActionNewline:
932  actionString = @"TextInputAction.newline";
933  break;
934  }
935  [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
936  arguments:@[ @(client), actionString ]];
937 }
938 
939 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
940  showAutocorrectionPromptRectForStart:(NSUInteger)start
941  end:(NSUInteger)end
942  withClient:(int)client {
943  [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
944  arguments:@[ @(client), @(start), @(end) ]];
945 }
946 
947 #pragma mark - FlutterViewEngineDelegate
948 
949 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView showToolbar:(int)client {
950  [_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
951 }
952 
953 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
954  focusElement:(UIScribbleElementIdentifier)elementIdentifier
955  atPoint:(CGPoint)referencePoint
957  [_textInputChannel.get()
958  invokeMethod:@"TextInputClient.focusElement"
959  arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
960  result:callback];
961 }
962 
963 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
964  requestElementsInRect:(CGRect)rect
966  [_textInputChannel.get()
967  invokeMethod:@"TextInputClient.requestElementsInRect"
968  arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
969  result:callback];
970 }
971 
972 - (void)flutterTextInputViewScribbleInteractionBegan:(FlutterTextInputView*)textInputView {
973  [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
974 }
975 
976 - (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView {
977  [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished"
978  arguments:nil];
979 }
980 
981 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
982  insertTextPlaceholderWithSize:(CGSize)size
983  withClient:(int)client {
984  [_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder"
985  arguments:@[ @(client), @(size.width), @(size.height) ]];
986 }
987 
988 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
989  removeTextPlaceholder:(int)client {
990  [_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder"
991  arguments:@[ @(client) ]];
992 }
993 
994 - (void)flutterTextInputViewDidResignFirstResponder:(FlutterTextInputView*)textInputView {
995  // Platform view's first responder detection logic:
996  //
997  // All text input widgets (e.g. EditableText) are backed by a dummy UITextInput view
998  // in the TextInputPlugin. When this dummy UITextInput view resigns first responder,
999  // check if any platform view becomes first responder. If any platform view becomes
1000  // first responder, send a "viewFocused" channel message to inform the framework to un-focus
1001  // the previously focused text input.
1002  //
1003  // Caveat:
1004  // 1. This detection logic does not cover the scenario when a platform view becomes
1005  // first responder without any flutter text input resigning its first responder status
1006  // (e.g. user tapping on platform view first). For now it works fine because the TextInputPlugin
1007  // does not track the focused platform view id (which is different from Android implementation).
1008  //
1009  // 2. This detection logic assumes that all text input widgets are backed by a dummy
1010  // UITextInput view in the TextInputPlugin, which may not hold true in the future.
1011 
1012  // Have to check in the next run loop, because iOS requests the previous first responder to
1013  // resign before requesting the next view to become first responder.
1014  dispatch_async(dispatch_get_main_queue(), ^(void) {
1015  long platform_view_id = self.platformViewsController->FindFirstResponderPlatformViewId();
1016  if (platform_view_id == -1) {
1017  return;
1018  }
1019 
1020  [_platformViewsChannel.get() invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1021  });
1022 }
1023 
1024 #pragma mark - Undo Manager Delegate
1025 
1026 - (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin
1027  handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1028  NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
1029  [_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1030 }
1031 
1032 #pragma mark - Screenshot Delegate
1033 
1035  asBase64Encoded:(BOOL)base64Encode {
1036  FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell";
1037  return _shell->Screenshot(type, base64Encode);
1038 }
1039 
1040 - (void)flutterViewAccessibilityDidCall {
1041  if (self.viewController.view.accessibilityElements == nil) {
1042  [self ensureSemanticsEnabled];
1043  }
1044 }
1045 
1046 - (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
1047  return _binaryMessenger;
1048 }
1049 
1050 // For test only. Ideally we should create a dependency injector for all dependencies and
1051 // remove this.
1052 - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
1053  // Discard the previous messenger and keep the new one.
1054  _binaryMessenger.parent = nil;
1055  [_binaryMessenger release];
1056  _binaryMessenger = [binaryMessenger retain];
1057 }
1058 
1059 #pragma mark - FlutterBinaryMessenger
1060 
1061 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1062  [self sendOnChannel:channel message:message binaryReply:nil];
1063 }
1064 
1065 - (void)sendOnChannel:(NSString*)channel
1066  message:(NSData*)message
1067  binaryReply:(FlutterBinaryReply)callback {
1068  NSParameterAssert(channel);
1069  NSAssert(_shell && _shell->IsSetup(),
1070  @"Sending a message before the FlutterEngine has been run.");
1072  (callback == nil) ? nullptr
1073  : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1074  ^(NSData* reply) {
1075  callback(reply);
1076  },
1077  _shell->GetTaskRunners().GetPlatformTaskRunner());
1078  std::unique_ptr<flutter::PlatformMessage> platformMessage =
1079  (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1080  : std::make_unique<flutter::PlatformMessage>(
1081  channel.UTF8String, flutter::CopyNSDataToMapping(message), response);
1082 
1083  _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1084  // platformMessage takes ownership of response.
1085  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1086 }
1087 
1088 - (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
1090 }
1091 
1092 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
1093  binaryMessageHandler:
1094  (FlutterBinaryMessageHandler)handler {
1095  return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1096 }
1097 
1099  setMessageHandlerOnChannel:(NSString*)channel
1100  binaryMessageHandler:(FlutterBinaryMessageHandler)handler
1101  taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
1102  NSParameterAssert(channel);
1103  if (_shell && _shell->IsSetup()) {
1104  self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1105  handler, taskQueue);
1106  return _connections->AquireConnection(channel.UTF8String);
1107  } else {
1108  NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
1109  // Setting a handler to nil for a channel that has not yet been set up is a no-op.
1111  }
1112 }
1113 
1114 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1115  if (_shell && _shell->IsSetup()) {
1116  std::string channel = _connections->CleanupConnection(connection);
1117  if (!channel.empty()) {
1118  self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1119  nil);
1120  }
1121  }
1122 }
1123 
1124 #pragma mark - FlutterTextureRegistry
1125 
1126 - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
1127  int64_t textureId = _nextTextureId++;
1128  self.iosPlatformView->RegisterExternalTexture(textureId, texture);
1129  return textureId;
1130 }
1131 
1132 - (void)unregisterTexture:(int64_t)textureId {
1133  _shell->GetPlatformView()->UnregisterTexture(textureId);
1134 }
1135 
1136 - (void)textureFrameAvailable:(int64_t)textureId {
1137  _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1138 }
1139 
1140 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1141  return [FlutterDartProject lookupKeyForAsset:asset];
1142 }
1143 
1144 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1145  return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
1146 }
1147 
1148 - (id<FlutterPluginRegistry>)pluginRegistry {
1149  return self;
1150 }
1151 
1152 #pragma mark - FlutterPluginRegistry
1153 
1154 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
1155  NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
1156  self.pluginPublications[pluginKey] = [NSNull null];
1157  FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
1158  flutterEngine:self];
1159  self.registrars[pluginKey] = result;
1160  return [result autorelease];
1161 }
1162 
1163 - (BOOL)hasPlugin:(NSString*)pluginKey {
1164  return _pluginPublications[pluginKey] != nil;
1165 }
1166 
1167 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1168  return _pluginPublications[pluginKey];
1169 }
1170 
1171 #pragma mark - Notifications
1172 
1173 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1174  [self setIsGpuDisabled:NO];
1175 }
1176 
1177 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1178  [self setIsGpuDisabled:YES];
1179  [self notifyLowMemory];
1180 }
1181 
1182 - (void)onMemoryWarning:(NSNotification*)notification {
1183  [self notifyLowMemory];
1184 }
1185 
1186 - (void)setIsGpuDisabled:(BOOL)value {
1187  if (_shell) {
1190  }
1191  _isGpuDisabled = value;
1192 }
1193 
1194 #pragma mark - Locale updates
1195 
1196 - (void)onLocaleUpdated:(NSNotification*)notification {
1197  // Get and pass the user's preferred locale list to dart:ui.
1198  NSMutableArray<NSString*>* localeData = [[[NSMutableArray alloc] init] autorelease];
1199  NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1200  for (NSString* localeID in preferredLocales) {
1201  NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
1202  NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1203  NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1204  NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1205  NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1206  if (!languageCode) {
1207  continue;
1208  }
1209  [localeData addObject:languageCode];
1210  [localeData addObject:(countryCode ? countryCode : @"")];
1211  [localeData addObject:(scriptCode ? scriptCode : @"")];
1212  [localeData addObject:(variantCode ? variantCode : @"")];
1213  }
1214  if (localeData.count == 0) {
1215  return;
1216  }
1217  [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1218 }
1219 
1220 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1221  callback:(void (^_Nonnull)(BOOL didTimeout))callback {
1222  dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1223  dispatch_async(queue, ^{
1224  fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1225  BOOL didTimeout =
1226  self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
1227  dispatch_async(dispatch_get_main_queue(), ^{
1228  callback(didTimeout);
1229  });
1230  });
1231 }
1232 
1233 - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
1234  libraryURI:(/*nullable*/ NSString*)libraryURI
1235  initialRoute:(/*nullable*/ NSString*)initialRoute
1236  entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
1237  NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
1238  FlutterEngine* result = [[FlutterEngine alloc] initWithName:_labelPrefix
1239  project:_dartProject.get()
1240  allowHeadlessExecution:_allowHeadlessExecution];
1241  flutter::RunConfiguration configuration =
1242  [_dartProject.get() runConfigurationForEntrypoint:entrypoint
1243  libraryOrNil:libraryURI
1244  entrypointArgs:entrypointArgs];
1245 
1247  FML_DCHECK(platform_view);
1248  // Static-cast safe since this class always creates PlatformViewIOS instances.
1249  flutter::PlatformViewIOS* ios_platform_view =
1250  static_cast<flutter::PlatformViewIOS*>(platform_view.get());
1251  std::shared_ptr<flutter::IOSContext> context = ios_platform_view->GetIosContext();
1252  FML_DCHECK(context);
1253 
1254  // Lambda captures by pointers to ObjC objects are fine here because the
1255  // create call is synchronous.
1257  [result, context](flutter::Shell& shell) {
1258  [result recreatePlatformViewController];
1259  return std::make_unique<flutter::PlatformViewIOS>(
1260  shell, context, result->_platformViewsController, shell.GetTaskRunners());
1261  };
1262 
1264  [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
1265 
1266  std::string cppInitialRoute;
1267  if (initialRoute) {
1268  cppInitialRoute = [initialRoute UTF8String];
1269  }
1270 
1271  std::unique_ptr<flutter::Shell> shell = _shell->Spawn(
1272  std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1273 
1274  result->_threadHost = _threadHost;
1275  result->_profiler = _profiler;
1276  result->_profiler_metrics = _profiler_metrics;
1277  result->_isGpuDisabled = _isGpuDisabled;
1278  [result setupShell:std::move(shell) withObservatoryPublication:NO];
1279  return [result autorelease];
1280 }
1281 
1282 - (const flutter::ThreadHost&)threadHost {
1283  return *_threadHost;
1284 }
1285 
1286 @end
1287 
1288 @implementation FlutterEngineRegistrar {
1289  NSString* _pluginKey;
1290 }
1291 
1292 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
1293  self = [super init];
1294  NSAssert(self, @"Super init cannot be nil");
1295  _pluginKey = [pluginKey copy];
1297  return self;
1298 }
1299 
1300 - (void)dealloc {
1301  [_pluginKey release];
1302  [super dealloc];
1303 }
1304 
1305 - (NSObject<FlutterBinaryMessenger>*)messenger {
1307 }
1308 
1309 - (NSObject<FlutterTextureRegistry>*)textures {
1310  return _flutterEngine;
1311 }
1312 
1313 - (void)publish:(NSObject*)value {
1314  _flutterEngine.pluginPublications[_pluginKey] = value;
1315 }
1316 
1317 - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
1318  channel:(FlutterMethodChannel*)channel {
1319  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1320  [delegate handleMethodCall:call result:result];
1321  }];
1322 }
1323 
1324 - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
1325  id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1326  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
1327  id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1328  (id<FlutterAppLifeCycleProvider>)appDelegate;
1329  [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1330  }
1331 }
1332 
1333 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1334  return [_flutterEngine lookupKeyForAsset:asset];
1335 }
1336 
1337 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1338  return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1339 }
1340 
1341 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1342  withId:(NSString*)factoryId {
1343  [self registerViewFactory:factory
1344  withId:factoryId
1345  gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1346 }
1347 
1348 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1349  withId:(NSString*)factoryId
1350  gestureRecognizersBlockingPolicy:
1351  (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
1352  [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
1353  gestureRecognizersBlockingPolicy);
1354 }
1355 
1356 @end
uint64_t timestamp
Definition: key_data.h:36
BOOL forceSoftwareRendering
Definition: FlutterView.h:47
fml::scoped_nsobject< FlutterTextInputPlugin > _textInputPlugin
uint64_t synthesized
Definition: key_data.h:43
std::string advisory_script_entrypoint
Definition: settings.h:158
VkQueue queue
Definition: main.cc:53
BOOL _allowHeadlessExecution
Maintains a current integer assigned to a name (connections).
void handleMethodCall:result:(FlutterMethodCall *call, [result] FlutterResult result)
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition: shell.h:116
Default priority level.
id< FlutterIndirectScribbleDelegate > indirectScribbleDelegate
std::unique_ptr< fml::WeakPtrFactory< FlutterEngine > > _weakFactory
std::shared_ptr< flutter::SamplingProfiler > _profiler
NSObject< FlutterBinaryMessenger > * binaryMessenger
#define FML_DCHECK(condition)
Definition: logging.h:86
FlutterViewController * viewController
uint64_t logical
Definition: key_data.h:39
std::optional< ThreadConfig > io_config
Definition: thread_host.h:78
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
std::string advisory_script_uri
Definition: settings.h:155
static void SetCurrentThreadName(const ThreadConfig &config)
Definition: thread.cc:66
fml::scoped_nsobject< FlutterMethodChannel > _undoManagerChannel
The ThreadConfig is the thread info include thread name, thread priority.
Definition: thread.h:34
std::unique_ptr< flutter::Shell > _shell
fml::scoped_nsobject< FlutterBasicMessageChannel > _keyEventChannel
std::unique_ptr< flutter::ConnectionCollection > _connections
fml::scoped_nsobject< FlutterPlatformPlugin > _platformPlugin
NSString *const kFlutterEngineWillDealloc
FlutterTextInputPlugin * textInputPlugin
enum flutter::testing::@1969::KeyboardChange::Type type
void reset(NST object=nil)
BOOL _restorationEnabled
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
char32_t character
static void EnsureInitializedForCurrentThread()
Definition: message_loop.cc:27
fml::scoped_nsobject< FlutterRestorationPlugin > _restorationPlugin
GAsyncResult * result
Win32Message message
NSString * isolateId
int64_t _nextTextureId
#define FML_LOG(severity)
Definition: logging.h:65
fml::scoped_nsobject< FlutterMethodChannel > _platformViewsChannel
fml::scoped_nsobject< FlutterUndoManagerPlugin > _undoManagerPlugin
std::optional< ThreadConfig > raster_config
Definition: thread_host.h:77
fml::scoped_nsobject< FlutterObservatoryPublisher > _publisher
fml::scoped_nsobject< FlutterMethodChannel > _localizationChannel
FlKeyEvent * event
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition: message_loop.cc:56
void setupIndirectScribbleInteraction:(id< FlutterViewResponder > viewResponder)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable Disable the Dart Observatory The observatory is never available in release mode Bind to the IPv6 localhost address for the Dart Observatory Ignored if observatory host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent Remove all existing persistent cache This is mainly for debugging purposes such as reproducing the shader compilation jank use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:226
static std::string MakeThreadName(Type type, const std::string &prefix)
Use the prefix and thread type to generator a thread name.
Definition: thread_host.cc:15
instancetype sharedInstance()
fml::Thread::ThreadConfig ThreadConfig
Definition: thread_host.h:17
std::shared_ptr< flutter::FlutterPlatformViewsController > _platformViewsController
Indicates that GPU operations should be permitted.
A POD type used to return the screenshot data along with the size of the frame.
Definition: rasterizer.h:281
uint8_t value
T * get() const
Definition: weak_ptr.h:89
static NSObject< FlutterTaskQueue > * MakeBackgroundTaskQueue()
fml::MallocMapping CopyNSDataToMapping(NSData *data)
ScreenshotType
The type of the screenshot to obtain of the previously rendered layer tree.
Definition: rasterizer.h:251
static constexpr int kNumProfilerSamplesPerSec
Specifies all the configuration required by the runtime library to launch the root isolate...
KeyEventType type
Definition: key_data.h:37
instancetype initWithName:project:allowHeadlessExecution:restorationEnabled:(NSString *labelPrefix, [project] nullable FlutterDartProject *project, [allowHeadlessExecution] BOOL allowHeadlessExecution, [restorationEnabled] BOOL NS_DESIGNATED_INITIALIZER)
BOOL runWithEntrypoint:libraryURI:initialRoute:(nullable NSString *entrypoint, [libraryURI] nullable NSString *libraryURI, [initialRoute] nullable NSString *initialRoute)
Suitable for threads which generate data for the display.
SemanticsAction action
NSString *const kFlutterKeyDataChannel
The collection of all the threads used by the engine.
Definition: thread_host.h:21
NSString *const FlutterDefaultDartEntrypoint
BOOL runWithEntrypoint:libraryURI:initialRoute:entrypointArgs:(nullable NSString *entrypoint, [libraryURI] nullable NSString *libraryURI, [initialRoute] nullable NSString *initialRoute, [entrypointArgs] nullable NSArray< NSString * > *entrypointArgs)
void(^ FlutterResult)(id _Nullable result)
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, TaskRunners task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer, bool is_gpu_disabled=false)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition: shell.cc:125
NSString * lookupKeyForAsset:fromPackage:(NSString *asset, [fromPackage] NSString *package)
fml::scoped_nsobject< FlutterMethodChannel > _platformChannel
size_t length
void ensureSemanticsEnabled()
bool EnableTracingIfNecessary(const Settings &vm_settings)
Enables tracing in the process so that JIT mode VMs may be launched. Explicitly enabling tracing is n...
Definition: ptrace_check.h:45
NSString * lookupKeyForAsset:(NSString *asset)
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
instancetype initWithName:project:allowHeadlessExecution:(NSString *labelPrefix, [project] nullable FlutterDartProject *project, [allowHeadlessExecution] BOOL allowHeadlessExecution)
FlutterPlatformViewGestureRecognizersBlockingPolicy
UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0))
int BOOL
Definition: windows_types.h:37
fml::WeakPtr< FlutterViewController > _viewController
NSString * _labelPrefix
NSString *const FlutterDefaultInitialRoute
ThreadPriority priority
Definition: thread.h:44
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
void sendMessage:(id _Nullable message)
uint64_t physical
Definition: key_data.h:38
fml::scoped_nsobject< FlutterMethodChannel > _textInputChannel
Suitable for thread which raster data.
static Connection MakeErrorConnection(int errCode)
void(* FlutterKeyEventCallback)(bool, void *)
Definition: embedder.h:887
FlTexture * texture
Suitable for threads that shouldn&#39;t disrupt high priority work.
void handleMethodCall:result:(FlutterMethodCall *call, [result] FlutterResult result)
return FALSE
std::shared_ptr< flutter::ProfilerMetricsIOS > _profiler_metrics
FlutterBasicMessageChannel * lifecycleChannel
int64_t FlutterBinaryMessengerConnection
fml::scoped_nsobject< FlutterBasicMessageChannel > _lifecycleChannel
std::shared_ptr< flutter::ThreadHost > _threadHost
fml::scoped_nsobject< FlutterMethodChannel > _restorationChannel
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
flutter::IOSRenderingAPI _renderingApi
const std::shared_ptr< IOSContext > & GetIosContext()
bool enable_software_rendering
Definition: settings.h:264
void SetOwnerViewController(fml::WeakPtr< FlutterViewController > owner_controller)
std::optional< ThreadConfig > ui_config
Definition: thread_host.h:76
FlutterBinaryMessengerRelay * _binaryMessenger
AtkStateType state
fml::scoped_nsobject< FlutterBasicMessageChannel > _systemChannel
NSObject< FlutterBinaryMessenger > * parent
static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig &config)
fml::scoped_nsobject< FlutterMethodChannel > _navigationChannel
void handleMethodCall:result:(FlutterMethodCall *call, [result] FlutterResult result)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable Disable the Dart Observatory The observatory is never available in release mode Bind to the IPv6 localhost address for the Dart Observatory Ignored if observatory host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent Remove all existing persistent cache This is mainly for debugging purposes such as reproducing the shader compilation jank use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network policy
Definition: switches.h:215
FlutterViewController * viewController
FlutterEngine * flutterEngine
std::string route
Definition: settings.h:110
FlutterEngine * _flutterEngine
fml::scoped_nsobject< FlutterBasicMessageChannel > _settingsChannel