Flutter iOS Embedder
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 #include <UIKit/UIKit.h>
6 #include "common/settings.h"
7 #define FML_USED_ON_EMBEDDER
8 
10 
11 #include <memory>
12 
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"
46 
48 
49 /// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities
50 /// Using iOS platform thread API to configure thread priority
51 static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
52  // set thread name
53  fml::Thread::SetCurrentThreadName(config);
54 
55  // set thread priority
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];
60  break;
61  }
62  case fml::Thread::ThreadPriority::kNormal: {
63  pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
64  [[NSThread currentThread] setThreadPriority:0.5];
65  break;
66  }
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];
71  sched_param param;
72  int policy;
73  pthread_t thread = pthread_self();
74  if (!pthread_getschedparam(thread, &policy, &param)) {
75  param.sched_priority = 50;
76  pthread_setschedparam(thread, policy, &param);
77  }
78  break;
79  }
80  }
81 }
82 
83 #pragma mark - Public exported constants
84 
85 NSString* const FlutterDefaultDartEntrypoint = nil;
86 NSString* const FlutterDefaultInitialRoute = nil;
87 
88 #pragma mark - Internal constants
89 
90 NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
91 static constexpr int kNumProfilerSamplesPerSec = 5;
92 NSString* const kFlutterApplicationRegistrarKey = @"io.flutter.flutter.application_registrar";
93 
95 
96 @property(nonatomic, weak) FlutterEngine* flutterEngine;
97 @property(nonatomic, readonly) NSString* key;
98 
99 - (instancetype)initWithKey:(NSString*)key flutterEngine:(FlutterEngine*)flutterEngine;
100 
101 @end
102 
105 @end
106 
108 @end
109 
115 
116 #pragma mark - Properties
117 
118 @property(nonatomic, readonly) FlutterDartProject* dartProject;
119 @property(nonatomic, readonly, copy) NSString* labelPrefix;
120 @property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
121 @property(nonatomic, readonly, assign) BOOL restorationEnabled;
122 
123 @property(nonatomic, strong) FlutterPlatformViewsController* platformViewsController;
124 @property(nonatomic, strong) FlutterEnginePluginSceneLifeCycleDelegate* sceneLifeCycleDelegate;
125 
126 // Maintains a dictionary of plugin names that have registered with the engine. Used by
127 // FlutterEnginePluginRegistrar to implement a FlutterPluginRegistrar.
128 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
129 @property(nonatomic, readonly)
130  NSMutableDictionary<NSString*, FlutterEngineBaseRegistrar*>* registrars;
131 
132 @property(nonatomic, readwrite, copy) NSString* isolateId;
133 @property(nonatomic, copy) NSString* initialRoute;
134 @property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
135 @property(nonatomic, strong) FlutterDartVMServicePublisher* publisher;
136 @property(nonatomic, strong) FlutterConnectionCollection* connections;
137 @property(nonatomic, assign) int64_t nextTextureId;
138 
139 #pragma mark - Channel properties
140 
141 @property(nonatomic, strong) FlutterPlatformPlugin* platformPlugin;
142 @property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin;
143 @property(nonatomic, strong) FlutterUndoManagerPlugin* undoManagerPlugin;
144 @property(nonatomic, strong) FlutterSpellCheckPlugin* spellCheckPlugin;
145 @property(nonatomic, strong) FlutterRestorationPlugin* restorationPlugin;
146 @property(nonatomic, strong) FlutterMethodChannel* localizationChannel;
147 @property(nonatomic, strong) FlutterMethodChannel* navigationChannel;
148 @property(nonatomic, strong) FlutterMethodChannel* restorationChannel;
149 @property(nonatomic, strong) FlutterMethodChannel* platformChannel;
150 // This channel only sends status bar related events to the framework thus has
151 // no handlers.
152 @property(nonatomic, strong) FlutterMethodChannel* statusBarChannel;
153 @property(nonatomic, strong) FlutterMethodChannel* platformViewsChannel;
154 @property(nonatomic, strong) FlutterMethodChannel* textInputChannel;
155 @property(nonatomic, strong) FlutterMethodChannel* undoManagerChannel;
156 @property(nonatomic, strong) FlutterMethodChannel* scribbleChannel;
157 @property(nonatomic, strong) FlutterMethodChannel* spellCheckChannel;
158 @property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel;
159 @property(nonatomic, strong) FlutterBasicMessageChannel* systemChannel;
160 @property(nonatomic, strong) FlutterBasicMessageChannel* settingsChannel;
161 @property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel;
162 @property(nonatomic, strong) FlutterMethodChannel* screenshotChannel;
163 
164 #pragma mark - Embedder API properties
165 
166 @property(nonatomic, assign) BOOL enableEmbedderAPI;
167 // Function pointers for interacting with the embedder.h API.
168 @property(nonatomic) FlutterEngineProcTable& embedderAPI;
169 
170 @end
171 
173  FlutterEngine* _engine;
174  NSObject<FlutterApplicationRegistrar>* _appRegistrar;
175 }
176 
177 - (instancetype)initWithEngine:(FlutterEngine*)engine {
178  self = [super init];
179  if (self) {
180  _engine = engine;
181  _appRegistrar = [engine registrarForApplication:kFlutterApplicationRegistrarKey];
182  }
183  return self;
184 }
185 
186 - (NSObject<FlutterPluginRegistry>*)pluginRegistry {
187  return _engine;
188 }
189 
190 - (NSObject<FlutterApplicationRegistrar>*)applicationRegistrar {
191  return _appRegistrar;
192 }
193 @end
194 
195 @implementation FlutterEngine {
196  std::shared_ptr<flutter::ThreadHost> _threadHost;
197  std::unique_ptr<flutter::Shell> _shell;
198 
200  std::shared_ptr<flutter::SamplingProfiler> _profiler;
201 
204 }
205 
206 - (int64_t)engineIdentifier {
207  return reinterpret_cast<int64_t>((__bridge void*)self);
208 }
209 
210 - (instancetype)init {
211  return [self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
212 }
213 
214 - (instancetype)initWithName:(NSString*)labelPrefix {
215  return [self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
216 }
217 
218 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
219  return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
220 }
221 
222 - (instancetype)initWithName:(NSString*)labelPrefix
223  project:(FlutterDartProject*)project
224  allowHeadlessExecution:(BOOL)allowHeadlessExecution {
225  return [self initWithName:labelPrefix
226  project:project
227  allowHeadlessExecution:allowHeadlessExecution
228  restorationEnabled:NO];
229 }
230 
231 - (instancetype)initWithName:(NSString*)labelPrefix
232  project:(FlutterDartProject*)project
233  allowHeadlessExecution:(BOOL)allowHeadlessExecution
234  restorationEnabled:(BOOL)restorationEnabled {
235  self = [super init];
236  NSAssert(self, @"Super init cannot be nil");
237  NSAssert(labelPrefix, @"labelPrefix is required");
238 
239  _restorationEnabled = restorationEnabled;
240  _allowHeadlessExecution = allowHeadlessExecution;
241  _labelPrefix = [labelPrefix copy];
242  _dartProject = project ?: [[FlutterDartProject alloc] init];
243 
244  _enableEmbedderAPI = _dartProject.settings.enable_embedder_api;
245  if (_enableEmbedderAPI) {
246  NSLog(@"============== iOS: enable_embedder_api is on ==============");
247  _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
248  FlutterEngineGetProcAddresses(&_embedderAPI);
249  }
250 
251  if (!EnableTracingIfNecessary(_dartProject.settings)) {
252  NSLog(
253  @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
254  @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
255  @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
256  @"profile and release mode apps can be launched from the home screen.");
257  return nil;
258  }
259 
260  _pluginPublications = [[NSMutableDictionary alloc] init];
261  _registrars = [[NSMutableDictionary alloc] init];
262  [self recreatePlatformViewsController];
263  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
264  _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
265  _connections = [[FlutterConnectionCollection alloc] init];
266 
267  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
268  [center addObserver:self
269  selector:@selector(onMemoryWarning:)
270  name:UIApplicationDidReceiveMemoryWarningNotification
271  object:nil];
272 
273  [self setUpLifecycleNotifications:center];
274 
275  [center addObserver:self
276  selector:@selector(onLocaleUpdated:)
277  name:NSCurrentLocaleDidChangeNotification
278  object:nil];
279 
280  self.sceneLifeCycleDelegate = [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
281 
282  return self;
283 }
284 
285 + (FlutterEngine*)engineForIdentifier:(int64_t)identifier {
286  NSAssert([[NSThread currentThread] isMainThread], @"Must be called on the main thread.");
287  return (__bridge FlutterEngine*)reinterpret_cast<void*>(identifier);
288 }
289 
290 - (void)setUpLifecycleNotifications:(NSNotificationCenter*)center {
291  // If the application is not available, use the scene for lifecycle notifications if available.
292  [center addObserver:self
293  selector:@selector(sceneWillConnect:)
294  name:UISceneWillConnectNotification
295  object:nil];
297  [center addObserver:self
298  selector:@selector(sceneWillEnterForeground:)
299  name:UISceneWillEnterForegroundNotification
300  object:nil];
301  [center addObserver:self
302  selector:@selector(sceneDidEnterBackground:)
303  name:UISceneDidEnterBackgroundNotification
304  object:nil];
305  return;
306  }
307  [center addObserver:self
308  selector:@selector(applicationWillEnterForeground:)
309  name:UIApplicationWillEnterForegroundNotification
310  object:nil];
311  [center addObserver:self
312  selector:@selector(applicationDidEnterBackground:)
313  name:UIApplicationDidEnterBackgroundNotification
314  object:nil];
315 }
316 
317 - (void)sceneWillConnect:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
318  UIScene* scene = notification.object;
319  if (!FlutterSharedApplication.application.supportsMultipleScenes) {
320  // Since there is only one scene, we can assume that the FlutterEngine is within this scene and
321  // register it to the scene.
322  // The FlutterEngine needs to be registered with the scene when the scene connects in order for
323  // plugins to receive the `scene:willConnectToSession:options` event.
324  // If we want to support multi-window on iPad later, we may need to add a way for deveopers to
325  // register their FlutterEngine to the scene manually during this event.
326  FlutterPluginSceneLifeCycleDelegate* sceneLifeCycleDelegate =
327  [FlutterPluginSceneLifeCycleDelegate fromScene:scene];
328  if (sceneLifeCycleDelegate != nil) {
329  return [sceneLifeCycleDelegate engine:self receivedConnectNotificationFor:scene];
330  }
331  }
332 }
333 
334 - (void)recreatePlatformViewsController {
335  _renderingApi = flutter::GetRenderingAPIForProcess(/*force_software=*/false);
336  _platformViewsController = [[FlutterPlatformViewsController alloc] init];
337 }
338 
339 - (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
340  return _renderingApi;
341 }
342 
343 - (void)dealloc {
344  /// Notify plugins of dealloc. This should happen first in dealloc since the
345  /// plugins may be talking to things like the binaryMessenger.
346  [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
347  if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
348  FlutterEngineBaseRegistrar* registrar = self.registrars[key];
349  if ([registrar conformsToProtocol:@protocol(FlutterPluginRegistrar)]) {
350  [object detachFromEngineForRegistrar:((id<FlutterPluginRegistrar>)registrar)];
351  }
352  }
353  }];
354 
355  // nil out weak references.
356  // TODO(cbracken): https://github.com/flutter/flutter/issues/156222
357  // Ensure that FlutterEnginePluginRegistrar is using weak pointers, then eliminate this code.
358  [_registrars enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineBaseRegistrar* registrar,
359  BOOL* stop) {
360  registrar.flutterEngine = nil;
361  }];
362 
363  _binaryMessenger.parent = nil;
364  _textureRegistry.parent = nil;
365 
366  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
367  if (_flutterViewControllerWillDeallocObserver) {
368  [center removeObserver:_flutterViewControllerWillDeallocObserver];
369  }
370  [center removeObserver:self];
371 }
372 
373 - (flutter::Shell&)shell {
374  FML_DCHECK(_shell);
375  return *_shell;
376 }
377 
378 - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
379  if (!self.platformView) {
380  return;
381  }
382  self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
383 }
384 
385 - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
386  if (!self.platformView) {
387  return;
388  }
389  self.platformView->DispatchPointerDataPacket(std::move(packet));
390 }
391 
392 - (void)installFirstFrameCallback:(void (^)(void))block {
393  if (!self.platformView) {
394  return;
395  }
396 
397  __weak FlutterEngine* weakSelf = self;
398  self.platformView->SetNextFrameCallback([weakSelf, block] {
399  FlutterEngine* strongSelf = weakSelf;
400  if (!strongSelf) {
401  return;
402  }
403  FML_DCHECK(strongSelf.platformTaskRunner);
404  FML_DCHECK(strongSelf.rasterTaskRunner);
405  FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
406  // Get callback on raster thread and jump back to platform thread.
407  strongSelf.platformTaskRunner->PostTask([block]() { block(); });
408  });
409 }
410 
411 - (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
412  if (!self.platformView) {
413  return;
414  }
415  self.platformView->SetSemanticsEnabled(enabled);
416  self.platformView->SetAccessibilityFeatures(flags);
417 }
418 
419 - (void)notifyViewCreated {
420  if (!self.platformView) {
421  return;
422  }
423  self.platformView->NotifyCreated();
424 }
425 
426 - (void)notifyViewDestroyed {
427  if (!self.platformView) {
428  return;
429  }
430  self.platformView->NotifyDestroyed();
431 }
432 
433 - (flutter::PlatformViewIOS*)platformView {
434  if (!_shell) {
435  return nullptr;
436  }
437  return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get());
438 }
439 
440 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
441  if (!_shell) {
442  return {};
443  }
444  return _shell->GetTaskRunners().GetPlatformTaskRunner();
445 }
446 
447 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
448  if (!_shell) {
449  return {};
450  }
451  return _shell->GetTaskRunners().GetUITaskRunner();
452 }
453 
454 - (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
455  if (!_shell) {
456  return {};
457  }
458  return _shell->GetTaskRunners().GetRasterTaskRunner();
459 }
460 
461 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
462  callback:(FlutterKeyEventCallback)callback
463  userData:(void*)userData API_AVAILABLE(ios(13.4)) {
464  if (@available(iOS 13.4, *)) {
465  } else {
466  return;
467  }
468  if (!self.platformView) {
469  return;
470  }
471  const char* character = event.character;
472 
473  flutter::KeyData key_data;
474  key_data.Clear();
475  key_data.timestamp = (uint64_t)event.timestamp;
476  switch (event.type) {
477  case kFlutterKeyEventTypeUp:
478  key_data.type = flutter::KeyEventType::kUp;
479  break;
480  case kFlutterKeyEventTypeDown:
481  key_data.type = flutter::KeyEventType::kDown;
482  break;
483  case kFlutterKeyEventTypeRepeat:
484  key_data.type = flutter::KeyEventType::kRepeat;
485  break;
486  }
487  key_data.physical = event.physical;
488  key_data.logical = event.logical;
489  key_data.synthesized = event.synthesized;
490 
491  auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
492  NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
493 
494  auto response = ^(NSData* reply) {
495  if (callback == nullptr) {
496  return;
497  }
498  BOOL handled = FALSE;
499  if (reply.length == 1 && *reinterpret_cast<const uint8_t*>(reply.bytes) == 1) {
500  handled = TRUE;
501  }
502  callback(handled, userData);
503  };
504 
505  [self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
506 }
507 
508 - (void)ensureSemanticsEnabled {
509  if (!self.platformView) {
510  return;
511  }
512  self.platformView->SetSemanticsEnabled(true);
513 }
514 
515 - (void)setViewController:(FlutterViewController*)viewController {
516  FML_DCHECK(self.platformView);
517  _viewController = viewController;
518  self.platformView->SetOwnerViewController(_viewController);
519  [self maybeSetupPlatformViewChannels];
520  [self updateDisplays];
521  self.textInputPlugin.viewController = viewController;
522 
523  if (viewController) {
524  __weak __block FlutterEngine* weakSelf = self;
525  self.flutterViewControllerWillDeallocObserver =
526  [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
527  object:viewController
528  queue:[NSOperationQueue mainQueue]
529  usingBlock:^(NSNotification* note) {
530  [weakSelf notifyViewControllerDeallocated];
531  }];
532  } else {
533  self.flutterViewControllerWillDeallocObserver = nil;
534  [self notifyLowMemory];
535  }
536 }
537 
538 - (void)attachView {
539  FML_DCHECK(self.platformView);
540  self.platformView->attachView();
541 }
542 
543 - (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
544  if (observer != _flutterViewControllerWillDeallocObserver) {
545  if (_flutterViewControllerWillDeallocObserver) {
546  [[NSNotificationCenter defaultCenter]
547  removeObserver:_flutterViewControllerWillDeallocObserver];
548  }
549  _flutterViewControllerWillDeallocObserver = observer;
550  }
551 }
552 
553 - (void)notifyViewControllerDeallocated {
554  [self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
555  self.textInputPlugin.viewController = nil;
556  if (!self.allowHeadlessExecution) {
557  [self destroyContext];
558  } else if (self.platformView) {
559  self.platformView->SetOwnerViewController({});
560  }
561  [self.textInputPlugin resetViewResponder];
562  _viewController = nil;
563 }
564 
565 - (void)destroyContext {
566  [self resetChannels];
567  self.isolateId = nil;
568  _shell.reset();
569  _profiler.reset();
570  _threadHost.reset();
571  _platformViewsController = nil;
572 }
573 
574 - (NSURL*)vmServiceUrl {
575  return self.publisher.url;
576 }
577 
578 - (void)resetChannels {
579  self.localizationChannel = nil;
580  self.navigationChannel = nil;
581  self.restorationChannel = nil;
582  self.platformChannel = nil;
583  self.statusBarChannel = nil;
584  self.platformViewsChannel = nil;
585  self.textInputChannel = nil;
586  self.undoManagerChannel = nil;
587  self.scribbleChannel = nil;
588  self.lifecycleChannel = nil;
589  self.systemChannel = nil;
590  self.settingsChannel = nil;
591  self.keyEventChannel = nil;
592  self.spellCheckChannel = nil;
593 }
594 
595 - (void)startProfiler {
596  FML_DCHECK(!_threadHost->name_prefix.empty());
597  _profiler = std::make_shared<flutter::SamplingProfiler>(
598  _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
599  []() {
600  flutter::ProfilerMetricsIOS profiler_metrics;
601  return profiler_metrics.GenerateSample();
602  },
604  _profiler->Start();
605 }
606 
607 // If you add a channel, be sure to also update `resetChannels`.
608 // Channels get a reference to the engine, and therefore need manual
609 // cleanup for proper collection.
610 - (void)setUpChannels {
611  // This will be invoked once the shell is done setting up and the isolate ID
612  // for the UI isolate is available.
613  __weak FlutterEngine* weakSelf = self;
614  [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
615  binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
616  if (weakSelf) {
617  weakSelf.isolateId =
618  [[FlutterStringCodec sharedInstance] decode:message];
619  }
620  }];
621 
622  self.localizationChannel =
623  [[FlutterMethodChannel alloc] initWithName:@"flutter/localization"
624  binaryMessenger:self.binaryMessenger
625  codec:[FlutterJSONMethodCodec sharedInstance]];
626 
627  self.navigationChannel =
628  [[FlutterMethodChannel alloc] initWithName:@"flutter/navigation"
629  binaryMessenger:self.binaryMessenger
630  codec:[FlutterJSONMethodCodec sharedInstance]];
631 
632  if ([_initialRoute length] > 0) {
633  // Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
634  [self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
635  _initialRoute = nil;
636  }
637 
638  self.restorationChannel =
639  [[FlutterMethodChannel alloc] initWithName:@"flutter/restoration"
640  binaryMessenger:self.binaryMessenger
641  codec:[FlutterStandardMethodCodec sharedInstance]];
642 
643  self.platformChannel =
644  [[FlutterMethodChannel alloc] initWithName:@"flutter/platform"
645  binaryMessenger:self.binaryMessenger
646  codec:[FlutterJSONMethodCodec sharedInstance]];
647 
648  self.statusBarChannel =
649  [[FlutterMethodChannel alloc] initWithName:@"flutter/status_bar"
650  binaryMessenger:self.binaryMessenger
651  codec:[FlutterJSONMethodCodec sharedInstance]];
652  [self.statusBarChannel resizeChannelBuffer:0]; // No buffering.
653 
654  self.platformViewsChannel =
655  [[FlutterMethodChannel alloc] initWithName:@"flutter/platform_views"
656  binaryMessenger:self.binaryMessenger
657  codec:[FlutterStandardMethodCodec sharedInstance]];
658 
659  self.textInputChannel =
660  [[FlutterMethodChannel alloc] initWithName:@"flutter/textinput"
661  binaryMessenger:self.binaryMessenger
662  codec:[FlutterJSONMethodCodec sharedInstance]];
663 
664  self.undoManagerChannel =
665  [[FlutterMethodChannel alloc] initWithName:@"flutter/undomanager"
666  binaryMessenger:self.binaryMessenger
667  codec:[FlutterJSONMethodCodec sharedInstance]];
668 
669  self.scribbleChannel =
670  [[FlutterMethodChannel alloc] initWithName:@"flutter/scribble"
671  binaryMessenger:self.binaryMessenger
672  codec:[FlutterJSONMethodCodec sharedInstance]];
673 
674  self.spellCheckChannel =
675  [[FlutterMethodChannel alloc] initWithName:@"flutter/spellcheck"
676  binaryMessenger:self.binaryMessenger
677  codec:[FlutterStandardMethodCodec sharedInstance]];
678 
679  self.lifecycleChannel =
680  [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/lifecycle"
681  binaryMessenger:self.binaryMessenger
683 
684  self.systemChannel =
685  [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/system"
686  binaryMessenger:self.binaryMessenger
688 
689  self.settingsChannel =
690  [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/settings"
691  binaryMessenger:self.binaryMessenger
693 
694  self.keyEventChannel =
695  [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/keyevent"
696  binaryMessenger:self.binaryMessenger
698 
699  self.textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
700  self.textInputPlugin.indirectScribbleDelegate = self;
701  [self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
702 
703  self.undoManagerPlugin = [[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
704  self.platformPlugin = [[FlutterPlatformPlugin alloc] initWithEngine:self];
705 
706  self.restorationPlugin =
707  [[FlutterRestorationPlugin alloc] initWithChannel:self.restorationChannel
708  restorationEnabled:self.restorationEnabled];
709  self.spellCheckPlugin = [[FlutterSpellCheckPlugin alloc] init];
710 
711  self.screenshotChannel =
712  [[FlutterMethodChannel alloc] initWithName:@"flutter/screenshot"
713  binaryMessenger:self.binaryMessenger
714  codec:[FlutterStandardMethodCodec sharedInstance]];
715 
716  [self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
717  FlutterResult _Nonnull result) {
718  FlutterEngine* strongSelf = weakSelf;
719  if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
720  return result([FlutterError
721  errorWithCode:@"invalid_state"
722  message:@"Requesting screenshot while engine is not running."
723  details:nil]);
724  }
725  flutter::Rasterizer::Screenshot screenshot =
726  [strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
727  if (!screenshot.data) {
728  return result([FlutterError errorWithCode:@"failure"
729  message:@"Unable to get screenshot."
730  details:nil]);
731  }
732  // TODO(gaaclarke): Find way to eliminate this data copy.
733  NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
734  length:screenshot.data->size()];
735  NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
736  NSNumber* width = @(screenshot.frame_size.width);
737  NSNumber* height = @(screenshot.frame_size.height);
738  return result(@[ width, height, format ?: [NSNull null], data ]);
739  }];
740 }
741 
742 - (void)maybeSetupPlatformViewChannels {
743  if (_shell && self.shell.IsSetup()) {
744  __weak FlutterEngine* weakSelf = self;
745 
746  [self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
747  [weakSelf.platformPlugin handleMethodCall:call result:result];
748  }];
749 
750  [self.platformViewsChannel
751  setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
752  if (weakSelf) {
753  [weakSelf.platformViewsController onMethodCall:call result:result];
754  }
755  }];
756 
757  [self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
758  [weakSelf.textInputPlugin handleMethodCall:call result:result];
759  }];
760 
761  [self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
762  [weakSelf.undoManagerPlugin handleMethodCall:call result:result];
763  }];
764 
765  [self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
766  [weakSelf.spellCheckPlugin handleMethodCall:call result:result];
767  }];
768  }
769 }
770 
771 - (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type
772  base64Encode:(bool)base64Encode {
773  return self.shell.Screenshot(type, base64Encode);
774 }
775 
776 - (void)launchEngine:(NSString*)entrypoint
777  libraryURI:(NSString*)libraryOrNil
778  entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
779  // Launch the Dart application with the inferred run configuration.
780  flutter::RunConfiguration configuration =
781  [self.dartProject runConfigurationForEntrypoint:entrypoint
782  libraryOrNil:libraryOrNil
783  entrypointArgs:entrypointArgs];
784 
785  configuration.SetEngineId(self.engineIdentifier);
786  self.shell.RunEngine(std::move(configuration));
787 }
788 
789 - (void)setUpShell:(std::unique_ptr<flutter::Shell>)shell
790  withVMServicePublication:(BOOL)doesVMServicePublication {
791  _shell = std::move(shell);
792  [self setUpChannels];
793  [self onLocaleUpdated:nil];
794  [self updateDisplays];
795  self.publisher = [[FlutterDartVMServicePublisher alloc]
796  initWithEnableVMServicePublication:doesVMServicePublication];
797  [self maybeSetupPlatformViewChannels];
798  _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
799  : flutter::GpuAvailability::kAvailable);
800 }
801 
802 + (BOOL)isProfilerEnabled {
803  bool profilerEnabled = false;
804 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
805  (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
806  profilerEnabled = true;
807 #endif
808  return profilerEnabled;
809 }
810 
811 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
812  static size_t s_shellCount = 0;
813  return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
814 }
815 
816 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
817  const flutter::Settings& settings) {
818  // The current thread will be used as the platform thread. Ensure that the message loop is
819  // initialized.
820  fml::MessageLoop::EnsureInitializedForCurrentThread();
821 
822  uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
823  if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) {
824  threadHostType |= flutter::ThreadHost::Type::kUi;
825  }
826 
827  if ([FlutterEngine isProfilerEnabled]) {
828  threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
829  }
830 
831  flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
833 
834  host_config.ui_config =
835  fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
836  flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
837  fml::Thread::ThreadPriority::kDisplay);
838  host_config.raster_config =
839  fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
840  flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
841  fml::Thread::ThreadPriority::kRaster);
842 
843  host_config.io_config =
844  fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
845  flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
846  fml::Thread::ThreadPriority::kNormal);
847 
848  return (flutter::ThreadHost){host_config};
849 }
850 
851 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
852  if (libraryURI) {
853  FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library";
854  settings->advisory_script_entrypoint = entrypoint.UTF8String;
855  settings->advisory_script_uri = libraryURI.UTF8String;
856  } else if (entrypoint) {
857  settings->advisory_script_entrypoint = entrypoint.UTF8String;
858  settings->advisory_script_uri = std::string("main.dart");
859  } else {
860  settings->advisory_script_entrypoint = std::string("main");
861  settings->advisory_script_uri = std::string("main.dart");
862  }
863 }
864 
865 - (BOOL)createShell:(NSString*)entrypoint
866  libraryURI:(NSString*)libraryURI
867  initialRoute:(NSString*)initialRoute {
868  if (_shell != nullptr) {
869  [FlutterLogger logWarning:@"This FlutterEngine was already invoked."];
870  return NO;
871  }
872 
873  self.initialRoute = initialRoute;
874 
875  auto settings = [self.dartProject settings];
876  if (initialRoute != nil) {
877  self.initialRoute = initialRoute;
878  } else if (settings.route.empty() == false) {
879  self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
880  }
881 
882  auto platformData = [self.dartProject defaultPlatformData];
883 
884  SetEntryPoint(&settings, entrypoint, libraryURI);
885 
886  NSString* threadLabel = [FlutterEngine generateThreadLabel:self.labelPrefix];
887  _threadHost = std::make_shared<flutter::ThreadHost>();
888  *_threadHost = MakeThreadHost(threadLabel, settings);
889 
890  __weak FlutterEngine* weakSelf = self;
891  flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
892  [weakSelf](flutter::Shell& shell) {
893  FlutterEngine* strongSelf = weakSelf;
894  if (!strongSelf) {
895  return std::unique_ptr<flutter::PlatformViewIOS>();
896  }
897  [strongSelf recreatePlatformViewsController];
898  strongSelf.platformViewsController.taskRunner =
899  shell.GetTaskRunners().GetPlatformTaskRunner();
900  return std::make_unique<flutter::PlatformViewIOS>(
901  shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
902  shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
903  shell.GetIsGpuDisabledSyncSwitch());
904  };
905 
906  flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
907  [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
908 
909  fml::RefPtr<fml::TaskRunner> ui_runner;
910  if (settings.enable_impeller &&
911  settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) {
912  ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
913  } else {
914  ui_runner = _threadHost->ui_thread->GetTaskRunner();
915  }
916  flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
917  fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
918  _threadHost->raster_thread->GetTaskRunner(), // raster
919  ui_runner, // ui
920  _threadHost->io_thread->GetTaskRunner() // io
921  );
922 
923  // Disable GPU if the app or scene is running in the background.
924  self.isGpuDisabled = self.viewController
925  ? self.viewController.stateIsBackground
927  FlutterSharedApplication.application.applicationState ==
928  UIApplicationStateBackground;
929 
930  // Create the shell. This is a blocking operation.
931  std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
932  /*platform_data=*/platformData,
933  /*task_runners=*/task_runners,
934  /*settings=*/settings,
935  /*on_create_platform_view=*/on_create_platform_view,
936  /*on_create_rasterizer=*/on_create_rasterizer,
937  /*is_gpu_disabled=*/_isGpuDisabled);
938 
939  if (shell == nullptr) {
940  NSString* errorMessage = [NSString
941  stringWithFormat:@"Could not start a shell FlutterEngine with entrypoint: %@", entrypoint];
942  [FlutterLogger logError:errorMessage];
943  } else {
944  [self setUpShell:std::move(shell)
945  withVMServicePublication:settings.enable_vm_service_publication];
946  if ([FlutterEngine isProfilerEnabled]) {
947  [self startProfiler];
948  }
949  }
950 
951  return _shell != nullptr;
952 }
953 
954 - (BOOL)performImplicitEngineCallback {
955  id appDelegate = FlutterSharedApplication.application.delegate;
956  if ([appDelegate conformsToProtocol:@protocol(FlutterImplicitEngineDelegate)]) {
957  id<FlutterImplicitEngineDelegate> provider = (id<FlutterImplicitEngineDelegate>)appDelegate;
958  [provider didInitializeImplicitFlutterEngine:[[FlutterImplicitEngineBridgeImpl alloc]
959  initWithEngine:self]];
960  return YES;
961  }
962  return NO;
963 }
964 
965 - (void)updateDisplays {
966  if (!_shell) {
967  // Tests may do this.
968  return;
969  }
970  auto vsync_waiter = _shell->GetVsyncWaiter().lock();
971  auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
972  std::vector<std::unique_ptr<flutter::Display>> displays;
973  auto screen_size = UIScreen.mainScreen.nativeBounds.size;
974  auto scale = UIScreen.mainScreen.scale;
975  displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
976  0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
977  _shell->OnDisplayUpdates(std::move(displays));
978 }
979 
980 - (BOOL)run {
981  return [self runWithEntrypoint:FlutterDefaultDartEntrypoint
982  libraryURI:nil
983  initialRoute:FlutterDefaultInitialRoute];
984 }
985 
986 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
987  return [self runWithEntrypoint:entrypoint
988  libraryURI:libraryURI
989  initialRoute:FlutterDefaultInitialRoute];
990 }
991 
992 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
993  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
994 }
995 
996 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
997  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
998 }
999 
1000 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
1001  libraryURI:(NSString*)libraryURI
1002  initialRoute:(NSString*)initialRoute {
1003  return [self runWithEntrypoint:entrypoint
1004  libraryURI:libraryURI
1005  initialRoute:initialRoute
1006  entrypointArgs:nil];
1007 }
1008 
1009 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
1010  libraryURI:(NSString*)libraryURI
1011  initialRoute:(NSString*)initialRoute
1012  entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
1013  if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
1014  [self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
1015  }
1016 
1017  return _shell != nullptr;
1018 }
1019 
1020 - (void)notifyLowMemory {
1021  if (_shell) {
1022  _shell->NotifyLowMemoryWarning();
1023  }
1024  [self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
1025 }
1026 
1027 #pragma mark - Text input delegate
1028 
1029 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1030  updateEditingClient:(int)client
1031  withState:(NSDictionary*)state {
1032  [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
1033  arguments:@[ @(client), state ]];
1034 }
1035 
1036 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1037  updateEditingClient:(int)client
1038  withState:(NSDictionary*)state
1039  withTag:(NSString*)tag {
1040  [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
1041  arguments:@[ @(client), @{tag : state} ]];
1042 }
1043 
1044 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1045  updateEditingClient:(int)client
1046  withDelta:(NSDictionary*)delta {
1047  [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
1048  arguments:@[ @(client), delta ]];
1049 }
1050 
1051 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1052  updateFloatingCursor:(FlutterFloatingCursorDragState)state
1053  withClient:(int)client
1054  withPosition:(NSDictionary*)position {
1055  NSString* stateString;
1056  switch (state) {
1057  case FlutterFloatingCursorDragStateStart:
1058  stateString = @"FloatingCursorDragState.start";
1059  break;
1060  case FlutterFloatingCursorDragStateUpdate:
1061  stateString = @"FloatingCursorDragState.update";
1062  break;
1063  case FlutterFloatingCursorDragStateEnd:
1064  stateString = @"FloatingCursorDragState.end";
1065  break;
1066  }
1067  [self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
1068  arguments:@[ @(client), stateString, position ]];
1069 }
1070 
1071 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1072  performAction:(FlutterTextInputAction)action
1073  withClient:(int)client {
1074  NSString* actionString;
1075  switch (action) {
1076  case FlutterTextInputActionUnspecified:
1077  // Where did the term "unspecified" come from? iOS has a "default" and Android
1078  // has "unspecified." These 2 terms seem to mean the same thing but we need
1079  // to pick just one. "unspecified" was chosen because "default" is often a
1080  // reserved word in languages with switch statements (dart, java, etc).
1081  actionString = @"TextInputAction.unspecified";
1082  break;
1083  case FlutterTextInputActionDone:
1084  actionString = @"TextInputAction.done";
1085  break;
1086  case FlutterTextInputActionGo:
1087  actionString = @"TextInputAction.go";
1088  break;
1089  case FlutterTextInputActionSend:
1090  actionString = @"TextInputAction.send";
1091  break;
1092  case FlutterTextInputActionSearch:
1093  actionString = @"TextInputAction.search";
1094  break;
1095  case FlutterTextInputActionNext:
1096  actionString = @"TextInputAction.next";
1097  break;
1098  case FlutterTextInputActionContinue:
1099  actionString = @"TextInputAction.continueAction";
1100  break;
1101  case FlutterTextInputActionJoin:
1102  actionString = @"TextInputAction.join";
1103  break;
1104  case FlutterTextInputActionRoute:
1105  actionString = @"TextInputAction.route";
1106  break;
1107  case FlutterTextInputActionEmergencyCall:
1108  actionString = @"TextInputAction.emergencyCall";
1109  break;
1110  case FlutterTextInputActionNewline:
1111  actionString = @"TextInputAction.newline";
1112  break;
1113  }
1114  [self.textInputChannel invokeMethod:@"TextInputClient.performAction"
1115  arguments:@[ @(client), actionString ]];
1116 }
1117 
1118 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1119  showAutocorrectionPromptRectForStart:(NSUInteger)start
1120  end:(NSUInteger)end
1121  withClient:(int)client {
1122  [self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1123  arguments:@[ @(client), @(start), @(end) ]];
1124 }
1125 
1126 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1127  willDismissEditMenuWithTextInputClient:(int)client {
1128  [self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1129  arguments:@[ @(client) ]];
1130 }
1131 
1132 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1133  shareSelectedText:(NSString*)selectedText {
1134  [self.platformPlugin showShareViewController:selectedText];
1135 }
1136 
1137 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1138  searchWebWithSelectedText:(NSString*)selectedText {
1139  [self.platformPlugin searchWeb:selectedText];
1140 }
1141 
1142 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1143  lookUpSelectedText:(NSString*)selectedText {
1144  [self.platformPlugin showLookUpViewController:selectedText];
1145 }
1146 
1147 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1148  performContextMenuCustomActionWithActionID:(NSString*)actionID
1149  textInputClient:(int)client {
1150  [self.platformChannel invokeMethod:@"ContextMenu.onPerformCustomAction"
1151  arguments:@[ @(client), actionID ]];
1152 }
1153 
1154 #pragma mark - FlutterViewEngineDelegate
1155 
1156 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView showToolbar:(int)client {
1157  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1158  // the framework has finished transitioning to the Scribble channel.
1159  // https://github.com/flutter/flutter/pull/115296
1160  [self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1161 }
1162 
1163 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1164  focusElement:(UIScribbleElementIdentifier)elementIdentifier
1165  atPoint:(CGPoint)referencePoint
1166  result:(FlutterResult)callback {
1167  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1168  // the framework has finished transitioning to the Scribble channel.
1169  // https://github.com/flutter/flutter/pull/115296
1170  [self.textInputChannel
1171  invokeMethod:@"TextInputClient.focusElement"
1172  arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1173  result:callback];
1174 }
1175 
1176 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1177  requestElementsInRect:(CGRect)rect
1178  result:(FlutterResult)callback {
1179  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1180  // the framework has finished transitioning to the Scribble channel.
1181  // https://github.com/flutter/flutter/pull/115296
1182  [self.textInputChannel
1183  invokeMethod:@"TextInputClient.requestElementsInRect"
1184  arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1185  result:callback];
1186 }
1187 
1188 - (void)flutterTextInputViewScribbleInteractionBegan:(FlutterTextInputView*)textInputView {
1189  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1190  // the framework has finished transitioning to the Scribble channel.
1191  // https://github.com/flutter/flutter/pull/115296
1192  [self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1193 }
1194 
1195 - (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView {
1196  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1197  // the framework has finished transitioning to the Scribble channel.
1198  // https://github.com/flutter/flutter/pull/115296
1199  [self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
1200 }
1201 
1202 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1203  insertTextPlaceholderWithSize:(CGSize)size
1204  withClient:(int)client {
1205  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1206  // the framework has finished transitioning to the Scribble channel.
1207  // https://github.com/flutter/flutter/pull/115296
1208  [self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
1209  arguments:@[ @(client), @(size.width), @(size.height) ]];
1210 }
1211 
1212 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1213  removeTextPlaceholder:(int)client {
1214  // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1215  // the framework has finished transitioning to the Scribble channel.
1216  // https://github.com/flutter/flutter/pull/115296
1217  [self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
1218  arguments:@[ @(client) ]];
1219 }
1220 
1221 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1222  didResignFirstResponderWithTextInputClient:(int)client {
1223  // When flutter text input view resign first responder, send a message to
1224  // framework to ensure the focus state is correct. This is useful when close
1225  // keyboard from platform side.
1226  [self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
1227  arguments:@[ @(client) ]];
1228 
1229  // Platform view's first responder detection logic:
1230  //
1231  // All text input widgets (e.g. EditableText) are backed by a dummy UITextInput view
1232  // in the TextInputPlugin. When this dummy UITextInput view resigns first responder,
1233  // check if any platform view becomes first responder. If any platform view becomes
1234  // first responder, send a "viewFocused" channel message to inform the framework to un-focus
1235  // the previously focused text input.
1236  //
1237  // Caveat:
1238  // 1. This detection logic does not cover the scenario when a platform view becomes
1239  // first responder without any flutter text input resigning its first responder status
1240  // (e.g. user tapping on platform view first). For now it works fine because the TextInputPlugin
1241  // does not track the focused platform view id (which is different from Android implementation).
1242  //
1243  // 2. This detection logic assumes that all text input widgets are backed by a dummy
1244  // UITextInput view in the TextInputPlugin, which may not hold true in the future.
1245 
1246  // Have to check in the next run loop, because iOS requests the previous first responder to
1247  // resign before requesting the next view to become first responder.
1248  dispatch_async(dispatch_get_main_queue(), ^(void) {
1249  long platform_view_id = [self.platformViewsController firstResponderPlatformViewId];
1250  if (platform_view_id == -1) {
1251  return;
1252  }
1253 
1254  [self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1255  });
1256 }
1257 
1258 #pragma mark - Undo Manager Delegate
1259 
1260 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1261  NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
1262  [self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1263 }
1264 
1265 - (UIView<UITextInput>*)activeTextInputView {
1266  return [[self textInputPlugin] textInputView];
1267 }
1268 
1269 - (NSUndoManager*)undoManager {
1270  return self.viewController.undoManager;
1271 }
1272 
1273 #pragma mark - Screenshot Delegate
1274 
1275 - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
1276  asBase64Encoded:(BOOL)base64Encode {
1277  FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell";
1278  return _shell->Screenshot(type, base64Encode);
1279 }
1280 
1281 - (void)flutterViewAccessibilityDidCall {
1282  if (self.viewController.view.accessibilityElements == nil) {
1283  [self ensureSemanticsEnabled];
1284  }
1285 }
1286 
1287 - (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
1288  return _binaryMessenger;
1289 }
1290 
1291 - (NSObject<FlutterTextureRegistry>*)textureRegistry {
1292  return _textureRegistry;
1293 }
1294 
1295 // For test only. Ideally we should create a dependency injector for all dependencies and
1296 // remove this.
1297 - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
1298  // Discard the previous messenger and keep the new one.
1299  if (binaryMessenger != _binaryMessenger) {
1300  _binaryMessenger.parent = nil;
1301  _binaryMessenger = binaryMessenger;
1302  }
1303 }
1304 
1305 #pragma mark - FlutterBinaryMessenger
1306 
1307 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1308  [self sendOnChannel:channel message:message binaryReply:nil];
1309 }
1310 
1311 - (void)sendOnChannel:(NSString*)channel
1312  message:(NSData*)message
1313  binaryReply:(FlutterBinaryReply)callback {
1314  NSParameterAssert(channel);
1315  NSAssert(_shell && _shell->IsSetup(),
1316  @"Sending a message before the FlutterEngine has been run.");
1317  fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1318  (callback == nil) ? nullptr
1319  : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1320  ^(NSData* reply) {
1321  callback(reply);
1322  },
1323  _shell->GetTaskRunners().GetPlatformTaskRunner());
1324  std::unique_ptr<flutter::PlatformMessage> platformMessage =
1325  (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1326  : std::make_unique<flutter::PlatformMessage>(
1327  channel.UTF8String, flutter::CopyNSDataToMapping(message), response);
1328 
1329  _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1330  // platformMessage takes ownership of response.
1331  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1332 }
1333 
1334 - (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
1336 }
1337 
1338 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
1339  binaryMessageHandler:
1340  (FlutterBinaryMessageHandler)handler {
1341  return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1342 }
1343 
1345  setMessageHandlerOnChannel:(NSString*)channel
1346  binaryMessageHandler:(FlutterBinaryMessageHandler)handler
1347  taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
1348  NSParameterAssert(channel);
1349  if (_shell && _shell->IsSetup()) {
1350  self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1351  handler, taskQueue);
1352  return [self.connections acquireConnectionForChannel:channel];
1353  } else {
1354  NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
1355  // Setting a handler to nil for a channel that has not yet been set up is a no-op.
1356  return [FlutterConnectionCollection makeErrorConnectionWithErrorCode:-1L];
1357  }
1358 }
1359 
1360 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1361  if (_shell && _shell->IsSetup()) {
1362  NSString* channel = [self.connections cleanupConnectionWithID:connection];
1363  if (channel.length > 0) {
1364  self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String, nil,
1365  nil);
1366  }
1367  }
1368 }
1369 
1370 #pragma mark - FlutterTextureRegistry
1371 
1372 - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
1373  FML_DCHECK(self.platformView);
1374  int64_t textureId = self.nextTextureId++;
1375  self.platformView->RegisterExternalTexture(textureId, texture);
1376  return textureId;
1377 }
1378 
1379 - (void)unregisterTexture:(int64_t)textureId {
1380  _shell->GetPlatformView()->UnregisterTexture(textureId);
1381 }
1382 
1383 - (void)textureFrameAvailable:(int64_t)textureId {
1384  _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1385 }
1386 
1387 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1388  return [FlutterDartProject lookupKeyForAsset:asset];
1389 }
1390 
1391 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1392  return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
1393 }
1394 
1395 - (id<FlutterPluginRegistry>)pluginRegistry {
1396  return self;
1397 }
1398 
1399 #pragma mark - FlutterPluginRegistry
1400 
1401 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
1402  NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
1403  self.pluginPublications[pluginKey] = [NSNull null];
1404  FlutterEnginePluginRegistrar* result = [[FlutterEnginePluginRegistrar alloc] initWithKey:pluginKey
1405  flutterEngine:self];
1406  self.registrars[pluginKey] = result;
1407  return result;
1408 }
1409 
1410 - (NSObject<FlutterApplicationRegistrar>*)registrarForApplication:(NSString*)key {
1411  NSAssert(self.pluginPublications[key] == nil, @"Duplicate key: %@", key);
1412  self.pluginPublications[key] = [NSNull null];
1414  [[FlutterEngineApplicationRegistrar alloc] initWithKey:key flutterEngine:self];
1415  self.registrars[key] = result;
1416  return result;
1417 }
1418 
1419 - (BOOL)hasPlugin:(NSString*)pluginKey {
1420  return _pluginPublications[pluginKey] != nil;
1421 }
1422 
1423 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1424  return _pluginPublications[pluginKey];
1425 }
1426 
1427 - (void)addSceneLifeCycleDelegate:(NSObject<FlutterSceneLifeCycleDelegate>*)delegate {
1428  [self.sceneLifeCycleDelegate addDelegate:delegate];
1429 }
1430 
1431 #pragma mark - Notifications
1432 
1433 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1434  [self flutterWillEnterForeground:notification];
1435 }
1436 
1437 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1438  [self flutterDidEnterBackground:notification];
1439 }
1440 
1441 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1442  [self flutterWillEnterForeground:notification];
1443 }
1444 
1445 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1446  [self flutterDidEnterBackground:notification];
1447 }
1448 
1449 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1450  [self setIsGpuDisabled:NO];
1451 }
1452 
1453 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1454  [self setIsGpuDisabled:YES];
1455  [self notifyLowMemory];
1456 }
1457 
1458 - (void)onMemoryWarning:(NSNotification*)notification {
1459  [self notifyLowMemory];
1460 }
1461 
1462 - (void)setIsGpuDisabled:(BOOL)value {
1463  if (_shell) {
1464  _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1465  : flutter::GpuAvailability::kAvailable);
1466  }
1467  _isGpuDisabled = value;
1468 }
1469 
1470 #pragma mark - Locale updates
1471 
1472 - (void)onLocaleUpdated:(NSNotification*)notification {
1473  // Get and pass the user's preferred locale list to dart:ui.
1474  NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
1475  NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1476  for (NSString* localeID in preferredLocales) {
1477  NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1478  NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1479  NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1480  NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1481  NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1482  if (!languageCode) {
1483  continue;
1484  }
1485  [localeData addObject:languageCode];
1486  [localeData addObject:(countryCode ? countryCode : @"")];
1487  [localeData addObject:(scriptCode ? scriptCode : @"")];
1488  [localeData addObject:(variantCode ? variantCode : @"")];
1489  }
1490  if (localeData.count == 0) {
1491  return;
1492  }
1493  [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1494 }
1495 
1496 - (void)onStatusBarTap {
1497  // Called by FlutterViewController to notify the framework that a tap landed
1498  // on the status bar, and the most relevant vertical scroll view visible in the
1499  // app, if applicable, should scroll to top.
1500  [self.statusBarChannel invokeMethod:@"handleScrollToTop" arguments:nil];
1501 }
1502 
1503 - (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1504  callback:(NS_NOESCAPE void (^_Nonnull)(BOOL didTimeout))callback {
1505  fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1506  fml::Status status = self.shell.WaitForFirstFrame(waitTime);
1507  callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1508 }
1509 
1510 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1511  callback:(void (^_Nonnull)(BOOL didTimeout))callback {
1512  dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1513  dispatch_group_t group = dispatch_group_create();
1514 
1515  __weak FlutterEngine* weakSelf = self;
1516  __block BOOL didTimeout = NO;
1517  dispatch_group_async(group, queue, ^{
1518  FlutterEngine* strongSelf = weakSelf;
1519  if (!strongSelf) {
1520  return;
1521  }
1522 
1523  fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1524  fml::Status status = strongSelf.shell.WaitForFirstFrame(waitTime);
1525  didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
1526  });
1527 
1528  // Only execute the main queue task once the background task has completely finished executing.
1529  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1530  // Strongly capture self on the task dispatched to the main thread.
1531  //
1532  // When we capture weakSelf strongly in the above block on a background thread, we risk the
1533  // possibility that all other strong references to FlutterEngine go out of scope while the block
1534  // executes and that the engine is dealloc'ed at the end of the above block on a background
1535  // thread. FlutterEngine is not safe to release on any thread other than the main thread.
1536  //
1537  // self is never nil here since it's a strong reference that's verified non-nil above, but we
1538  // use a conditional check to avoid an unused expression compiler warning.
1539  FlutterEngine* strongSelf = self;
1540  if (!strongSelf) {
1541  return;
1542  }
1543  callback(didTimeout);
1544  });
1545 }
1546 
1547 - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
1548  libraryURI:(/*nullable*/ NSString*)libraryURI
1549  initialRoute:(/*nullable*/ NSString*)initialRoute
1550  entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
1551  NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
1552  FlutterEngine* result = [[FlutterEngine alloc] initWithName:self.labelPrefix
1553  project:self.dartProject
1554  allowHeadlessExecution:self.allowHeadlessExecution];
1555  flutter::RunConfiguration configuration =
1556  [self.dartProject runConfigurationForEntrypoint:entrypoint
1557  libraryOrNil:libraryURI
1558  entrypointArgs:entrypointArgs];
1559 
1560  configuration.SetEngineId(result.engineIdentifier);
1561 
1562  fml::WeakPtr<flutter::PlatformView> platform_view = _shell->GetPlatformView();
1563  FML_DCHECK(platform_view);
1564  // Static-cast safe since this class always creates PlatformViewIOS instances.
1565  flutter::PlatformViewIOS* ios_platform_view =
1566  static_cast<flutter::PlatformViewIOS*>(platform_view.get());
1567  std::shared_ptr<flutter::IOSContext> context = ios_platform_view->GetIosContext();
1568  FML_DCHECK(context);
1569 
1570  // Lambda captures by pointers to ObjC objects are fine here because the
1571  // create call is synchronous.
1572  flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1573  [result, context](flutter::Shell& shell) {
1574  [result recreatePlatformViewsController];
1575  result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
1576  return std::make_unique<flutter::PlatformViewIOS>(
1577  shell, context, result.platformViewsController, shell.GetTaskRunners());
1578  };
1579 
1580  flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1581  [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
1582 
1583  std::string cppInitialRoute;
1584  if (initialRoute) {
1585  cppInitialRoute = [initialRoute UTF8String];
1586  }
1587 
1588  std::unique_ptr<flutter::Shell> shell = _shell->Spawn(
1589  std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1590 
1591  result->_threadHost = _threadHost;
1592  result->_profiler = _profiler;
1593  result->_isGpuDisabled = _isGpuDisabled;
1594  [result setUpShell:std::move(shell) withVMServicePublication:NO];
1595  return result;
1596 }
1597 
1598 - (const flutter::ThreadHost&)threadHost {
1599  return *_threadHost;
1600 }
1601 
1602 - (FlutterDartProject*)project {
1603  return self.dartProject;
1604 }
1605 
1606 - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(void (^)(BOOL success))completion {
1607  __weak FlutterEngine* weakSelf = self;
1608  [self waitForFirstFrame:3.0
1609  callback:^(BOOL didTimeout) {
1610  if (didTimeout) {
1611  [FlutterLogger
1612  logError:@"Timeout waiting for first frame when launching a URL."];
1613  completion(NO);
1614  } else {
1615  // invove the method and get the result
1616  [weakSelf.navigationChannel
1617  invokeMethod:@"pushRouteInformation"
1618  arguments:@{
1619  @"location" : url.absoluteString ?: [NSNull null],
1620  }
1621  result:^(id _Nullable result) {
1622  BOOL success =
1623  [result isKindOfClass:[NSNumber class]] && [result boolValue];
1624  if (!success) {
1625  // Logging the error if the result is not successful
1626  [FlutterLogger
1627  logError:@"Failed to handle route information in Flutter."];
1628  }
1629  completion(success);
1630  }];
1631  }
1632  }];
1633 }
1634 
1635 @end
1636 
1637 @implementation FlutterEngineBaseRegistrar
1638 
1639 - (instancetype)initWithKey:(NSString*)key flutterEngine:(FlutterEngine*)flutterEngine {
1640  self = [super init];
1641  NSAssert(self, @"Super init cannot be nil");
1642  _key = [key copy];
1643  _flutterEngine = flutterEngine;
1644  return self;
1645 }
1646 
1647 - (NSObject<FlutterBinaryMessenger>*)messenger {
1648  return _flutterEngine.binaryMessenger;
1649 }
1650 - (NSObject<FlutterTextureRegistry>*)textures {
1651  return _flutterEngine.textureRegistry;
1652 }
1653 
1654 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1655  withId:(NSString*)factoryId {
1656  [self registerViewFactory:factory
1657  withId:factoryId
1658  gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1659 }
1660 
1661 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1662  withId:(NSString*)factoryId
1663  gestureRecognizersBlockingPolicy:
1664  (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
1665  [_flutterEngine.platformViewsController registerViewFactory:factory
1666  withId:factoryId
1667  gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
1668 }
1669 
1670 @end
1671 
1672 @implementation FlutterEnginePluginRegistrar
1673 
1674 - (nullable UIViewController*)viewController {
1675  return self.flutterEngine.viewController;
1676 }
1677 
1678 - (void)publish:(NSObject*)value {
1679  self.flutterEngine.pluginPublications[self.key] = value;
1680 }
1681 
1682 - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
1683  channel:(FlutterMethodChannel*)channel {
1684  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1685  [delegate handleMethodCall:call result:result];
1686  }];
1687 }
1688 
1689 - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
1690  id<UIApplicationDelegate> appDelegate = FlutterSharedApplication.application.delegate;
1691  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
1692  id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1693  (id<FlutterAppLifeCycleProvider>)appDelegate;
1694  [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1695  }
1696  if (![delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleDelegate)]) {
1697  // TODO(vashworth): If the plugin doesn't conform to the FlutterSceneLifeCycleDelegate,
1698  // print a warning pointing to documentation: https://github.com/flutter/flutter/issues/175956
1699  // [FlutterLogger logWarning:[NSString stringWithFormat:@"Plugin %@ has not migrated to
1700  // scenes.", self.key]];
1701  }
1702 }
1703 
1704 - (void)addSceneDelegate:(NSObject<FlutterSceneLifeCycleDelegate>*)delegate {
1705  // If the plugin conforms to FlutterSceneLifeCycleDelegate, add it to the engine.
1706  [self.flutterEngine addSceneLifeCycleDelegate:delegate];
1707 }
1708 
1709 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1710  return [self.flutterEngine lookupKeyForAsset:asset];
1711 }
1712 
1713 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1714  return [self.flutterEngine lookupKeyForAsset:asset fromPackage:package];
1715 }
1716 
1717 @end
1718 
1719 @implementation FlutterEngineApplicationRegistrar
1720 @end
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
NSObject< FlutterApplicationRegistrar > * _appRegistrar
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
NSString *const kFlutterApplicationRegistrarKey
FlutterBinaryMessengerRelay * _binaryMessenger
std::unique_ptr< flutter::PlatformViewIOS > platform_view
FlutterPlatformViewGestureRecognizersBlockingPolicy
BOOL _restorationEnabled
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
FlutterRestorationPlugin * restorationPlugin
NSMutableDictionary * pluginPublications
FlutterEngineProcTable & embedderAPI
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
const flutter::Settings & settings()
NSString * lookupKeyForAsset:(NSString *asset)
static NSObject< FlutterTaskQueue > * MakeBackgroundTaskQueue()
const std::shared_ptr< IOSContext > & GetIosContext()
void SetSemanticsEnabled(bool enabled) override
NSObject< FlutterBinaryMessenger > * parent
FlutterEngine * flutterEngine
FlutterMethodChannel * textInputChannel
flutter::PlatformViewIOS * platformView()
flutter::Shell & shell()
FlutterMethodChannel * navigationChannel
FlutterBasicMessageChannel * keyEventChannel
FlutterBasicMessageChannel * lifecycleChannel
FlutterMethodChannel * platformChannel
FlutterMethodChannel * localizationChannel
NSString * isolateId
FlutterBasicMessageChannel * systemChannel
FlutterBasicMessageChannel * settingsChannel
FlutterMethodChannel * restorationChannel
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
NSObject< FlutterTextureRegistry > * parent
fml::MallocMapping CopyNSDataToMapping(NSData *data)
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
NSObject< FlutterTextureRegistry > * textures()
NSObject< FlutterBinaryMessenger > * messenger()
instancetype sharedInstance()
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)