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