Flutter Engine
FlutterEngine.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define FML_USED_ON_EMBEDDER
6 
7 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
8 
9 #include <memory>
10 
11 #include "flutter/fml/message_loop.h"
12 #include "flutter/fml/platform/darwin/platform_version.h"
13 #include "flutter/fml/trace_event.h"
14 #include "flutter/runtime/ptrace_check.h"
15 #include "flutter/shell/common/engine.h"
16 #include "flutter/shell/common/platform_view.h"
17 #include "flutter/shell/common/shell.h"
18 #include "flutter/shell/common/switches.h"
19 #include "flutter/shell/common/thread_host.h"
20 #import "flutter/shell/platform/darwin/common/command_line.h"
21 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h"
22 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
23 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h"
24 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
25 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
26 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
27 #import "flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h"
28 #import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
29 #import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
30 #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
31 #import "flutter/shell/platform/darwin/ios/ios_context.h"
32 #import "flutter/shell/platform/darwin/ios/ios_surface.h"
33 #import "flutter/shell/platform/darwin/ios/ios_surface_factory.h"
34 #import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
35 #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
36 #include "flutter/shell/profiling/sampling_profiler.h"
37 
38 NSString* const FlutterDefaultDartEntrypoint = nil;
39 NSString* const FlutterDefaultInitialRoute = nil;
40 static constexpr int kNumProfilerSamplesPerSec = 5;
41 
42 @interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
43 @property(nonatomic, assign) FlutterEngine* flutterEngine;
44 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine;
45 @end
46 
47 @interface FlutterEngine () <FlutterTextInputDelegate, FlutterBinaryMessenger>
48 // Maintains a dictionary of plugin names that have registered with the engine. Used by
49 // FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
50 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
51 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
52 
53 @property(nonatomic, readwrite, copy) NSString* isolateId;
54 @property(nonatomic, copy) NSString* initialRoute;
55 @property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
56 @end
57 
58 @implementation FlutterEngine {
61  std::unique_ptr<flutter::Shell> _shell;
62  NSString* _labelPrefix;
63  std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;
64 
67 
68  std::shared_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
70  std::shared_ptr<flutter::IOSSurfaceFactory> _surfaceFactory;
71  std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
72  std::unique_ptr<flutter::SamplingProfiler> _profiler;
73 
74  // Channels
86 
87  int64_t _nextTextureId;
88 
91  std::unique_ptr<flutter::ConnectionCollection> _connections;
92 }
93 
94 - (instancetype)init {
95  return [self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
96 }
97 
98 - (instancetype)initWithName:(NSString*)labelPrefix {
99  return [self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
100 }
101 
102 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
103  return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
104 }
105 
106 - (instancetype)initWithName:(NSString*)labelPrefix
107  project:(FlutterDartProject*)project
108  allowHeadlessExecution:(BOOL)allowHeadlessExecution {
109  self = [super init];
110  NSAssert(self, @"Super init cannot be nil");
111  NSAssert(labelPrefix, @"labelPrefix is required");
112 
113  _allowHeadlessExecution = allowHeadlessExecution;
114  _labelPrefix = [labelPrefix copy];
115 
116  _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
117 
118  if (project == nil)
119  _dartProject.reset([[FlutterDartProject alloc] init]);
120  else
121  _dartProject.reset([project retain]);
122 
123  if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
124  NSLog(
125  @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
126  @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
127  @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
128  @"profile and release mode apps can be launched from the home screen.");
129  [self release];
130  return nil;
131  }
132 
133  _pluginPublications = [NSMutableDictionary new];
134  _registrars = [[NSMutableDictionary alloc] init];
135  [self recreatePlatformViewController];
136 
137  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
139 
140  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
141  [center addObserver:self
142  selector:@selector(onMemoryWarning:)
143  name:UIApplicationDidReceiveMemoryWarningNotification
144  object:nil];
145 
146  [center addObserver:self
147  selector:@selector(applicationDidEnterBackground:)
148  name:UIApplicationDidEnterBackgroundNotification
149  object:nil];
150 
151  [center addObserver:self
152  selector:@selector(applicationBecameActive:)
153  name:UIApplicationDidBecomeActiveNotification
154  object:nil];
155 
156  [center addObserver:self
157  selector:@selector(applicationWillResignActive:)
158  name:UIApplicationWillResignActiveNotification
159  object:nil];
160 
161  [center addObserver:self
162  selector:@selector(onLocaleUpdated:)
163  name:NSCurrentLocaleDidChangeNotification
164  object:nil];
165 
166  return self;
167 }
168 
169 - (void)recreatePlatformViewController {
173  _surfaceFactory->SetPlatformViewsController(_platformViewsController);
174 }
175 
176 - (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
177  return _renderingApi;
178 }
179 
180 - (void)dealloc {
181  /// Notify plugins of dealloc. This should happen first in dealloc since the
182  /// plugins may be talking to things like the binaryMessenger.
183  [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
184  if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
185  NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
186  [object detachFromEngineForRegistrar:registrar];
187  }
188  }];
189 
190  /// nil out weak references.
191  [_registrars
192  enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
193  registrar.flutterEngine = nil;
194  }];
195 
196  [_labelPrefix release];
197  [_initialRoute release];
198  [_pluginPublications release];
199  [_registrars release];
200  _binaryMessenger.parent = nil;
201  [_binaryMessenger release];
202 
203  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
204  if (_flutterViewControllerWillDeallocObserver) {
205  [center removeObserver:_flutterViewControllerWillDeallocObserver];
206  [_flutterViewControllerWillDeallocObserver release];
207  }
208  [center removeObserver:self];
209 
210  [super dealloc];
211 }
212 
213 - (flutter::Shell&)shell {
215  return *_shell;
216 }
217 
218 - (fml::WeakPtr<FlutterEngine>)getWeakPtr {
219  return _weakFactory->GetWeakPtr();
220 }
221 
222 - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
223  if (!self.platformView) {
224  return;
225  }
226  self.platformView->SetViewportMetrics(std::move(viewportMetrics));
227 }
228 
229 - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
230  if (!self.platformView) {
231  return;
232  }
233  self.platformView->DispatchPointerDataPacket(std::move(packet));
234 }
235 
236 - (fml::WeakPtr<flutter::PlatformView>)platformView {
238  return _shell->GetPlatformView();
239 }
240 
241 - (flutter::PlatformViewIOS*)iosPlatformView {
243  return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get());
244 }
245 
246 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
248  return _shell->GetTaskRunners().GetPlatformTaskRunner();
249 }
250 
251 - (fml::RefPtr<fml::TaskRunner>)RasterTaskRunner {
253  return _shell->GetTaskRunners().GetRasterTaskRunner();
254 }
255 
256 - (void)ensureSemanticsEnabled {
257  self.iosPlatformView->SetSemanticsEnabled(true);
258 }
259 
260 - (void)setViewController:(FlutterViewController*)viewController {
261  FML_DCHECK(self.iosPlatformView);
263  viewController ? [viewController getWeakPtr] : fml::WeakPtr<FlutterViewController>();
264  self.iosPlatformView->SetOwnerViewController(_viewController);
265  [self maybeSetupPlatformViewChannels];
266 
267  if (viewController) {
268  __block FlutterEngine* blockSelf = self;
269  self.flutterViewControllerWillDeallocObserver =
270  [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
271  object:viewController
272  queue:[NSOperationQueue mainQueue]
273  usingBlock:^(NSNotification* note) {
274  [blockSelf notifyViewControllerDeallocated];
275  }];
276  } else {
277  self.flutterViewControllerWillDeallocObserver = nil;
278  [self notifyLowMemory];
279  }
280 }
281 
282 - (void)attachView {
283  self.iosPlatformView->attachView();
284 }
285 
286 - (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
287  if (observer != _flutterViewControllerWillDeallocObserver) {
288  if (_flutterViewControllerWillDeallocObserver) {
289  [[NSNotificationCenter defaultCenter]
290  removeObserver:_flutterViewControllerWillDeallocObserver];
291  [_flutterViewControllerWillDeallocObserver release];
292  }
293  _flutterViewControllerWillDeallocObserver = [observer retain];
294  }
295 }
296 
297 - (void)notifyViewControllerDeallocated {
298  [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
300  [self destroyContext];
301  } else {
302  flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
303  if (platform_view) {
304  platform_view->SetOwnerViewController({});
305  }
306  }
307  _viewController.reset();
308 }
309 
310 - (void)destroyContext {
311  [self resetChannels];
312  self.isolateId = nil;
313  _shell.reset();
314  _profiler.reset();
315  _threadHost.Reset();
316  _platformViewsController.reset();
317 }
318 
319 - (FlutterViewController*)viewController {
320  if (!_viewController) {
321  return nil;
322  }
323  return _viewController.get();
324 }
325 
326 - (FlutterPlatformPlugin*)platformPlugin {
327  return _platformPlugin.get();
328 }
329 - (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController {
331 }
333  return _textInputPlugin.get();
334 }
335 - (FlutterMethodChannel*)localizationChannel {
336  return _localizationChannel.get();
337 }
338 - (FlutterMethodChannel*)navigationChannel {
339  return _navigationChannel.get();
340 }
341 - (FlutterMethodChannel*)platformChannel {
342  return _platformChannel.get();
343 }
344 - (FlutterMethodChannel*)textInputChannel {
345  return _textInputChannel.get();
346 }
347 - (FlutterBasicMessageChannel*)lifecycleChannel {
348  return _lifecycleChannel.get();
349 }
350 - (FlutterBasicMessageChannel*)systemChannel {
351  return _systemChannel.get();
352 }
353 - (FlutterBasicMessageChannel*)settingsChannel {
354  return _settingsChannel.get();
355 }
356 - (FlutterBasicMessageChannel*)keyEventChannel {
357  return _keyEventChannel.get();
358 }
359 
360 - (NSURL*)observatoryUrl {
361  return [_publisher.get() url];
362 }
363 
364 - (void)resetChannels {
370  _lifecycleChannel.reset();
371  _systemChannel.reset();
372  _settingsChannel.reset();
373  _keyEventChannel.reset();
374 }
375 
376 - (void)startProfiler:(NSString*)threadLabel {
377  _profiler_metrics = std::make_unique<flutter::ProfilerMetricsIOS>();
378  _profiler = std::make_unique<flutter::SamplingProfiler>(
379  threadLabel.UTF8String, _threadHost.profiler_thread->GetTaskRunner(),
380  [self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
381  _profiler->Start();
382 }
383 
384 // If you add a channel, be sure to also update `resetChannels`.
385 // Channels get a reference to the engine, and therefore need manual
386 // cleanup for proper collection.
387 - (void)setupChannels {
388  // This will be invoked once the shell is done setting up and the isolate ID
389  // for the UI isolate is available.
390  fml::WeakPtr<FlutterEngine> weakSelf = [self getWeakPtr];
391  [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
392  binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
393  if (weakSelf) {
394  weakSelf.get().isolateId =
395  [[FlutterStringCodec sharedInstance] decode:message];
396  }
397  }];
398 
400  initWithName:@"flutter/localization"
401  binaryMessenger:self.binaryMessenger
402  codec:[FlutterJSONMethodCodec sharedInstance]]);
403 
405  initWithName:@"flutter/navigation"
406  binaryMessenger:self.binaryMessenger
407  codec:[FlutterJSONMethodCodec sharedInstance]]);
408 
409  if ([_initialRoute length] > 0) {
410  // Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
411  [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
412  [_initialRoute release];
413  _initialRoute = nil;
414  }
415 
417  initWithName:@"flutter/platform"
418  binaryMessenger:self.binaryMessenger
419  codec:[FlutterJSONMethodCodec sharedInstance]]);
420 
422  initWithName:@"flutter/platform_views"
423  binaryMessenger:self.binaryMessenger
424  codec:[FlutterStandardMethodCodec sharedInstance]]);
425 
427  initWithName:@"flutter/textinput"
428  binaryMessenger:self.binaryMessenger
429  codec:[FlutterJSONMethodCodec sharedInstance]]);
430 
432  initWithName:@"flutter/lifecycle"
433  binaryMessenger:self.binaryMessenger
434  codec:[FlutterStringCodec sharedInstance]]);
435 
437  initWithName:@"flutter/system"
438  binaryMessenger:self.binaryMessenger
439  codec:[FlutterJSONMessageCodec sharedInstance]]);
440 
442  initWithName:@"flutter/settings"
443  binaryMessenger:self.binaryMessenger
444  codec:[FlutterJSONMessageCodec sharedInstance]]);
445 
447  initWithName:@"flutter/keyevent"
448  binaryMessenger:self.binaryMessenger
449  codec:[FlutterJSONMessageCodec sharedInstance]]);
450 
451  _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]);
452  _textInputPlugin.get().textInputDelegate = self;
453 
454  _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]);
455 }
456 
457 - (void)maybeSetupPlatformViewChannels {
458  if (_shell && self.shell.IsSetup()) {
459  FlutterPlatformPlugin* platformPlugin = _platformPlugin.get();
460  [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
461  [platformPlugin handleMethodCall:call result:result];
462  }];
463 
464  fml::WeakPtr<FlutterEngine> weakSelf = [self getWeakPtr];
465  [_platformViewsChannel.get()
466  setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
467  if (weakSelf) {
468  weakSelf.get().platformViewsController->OnMethodCall(call, result);
469  }
470  }];
471 
473  [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
474  [textInputPlugin handleMethodCall:call result:result];
475  }];
476  }
477 }
478 
480  base64Encode:(bool)base64Encode {
481  return self.shell.Screenshot(type, base64Encode);
482 }
483 
484 - (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
485  // Launch the Dart application with the inferred run configuration.
486  self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
487  libraryOrNil:libraryOrNil]);
488 }
489 
490 - (BOOL)createShell:(NSString*)entrypoint
491  libraryURI:(NSString*)libraryURI
492  initialRoute:(NSString*)initialRoute {
493  if (_shell != nullptr) {
494  FML_LOG(WARNING) << "This FlutterEngine was already invoked.";
495  return NO;
496  }
497 
498  static size_t shellCount = 1;
499  self.initialRoute = initialRoute;
500 
501  auto settings = [_dartProject.get() settings];
502  FlutterView.forceSoftwareRendering = settings.enable_software_rendering;
503 
504  auto platformData = [_dartProject.get() defaultPlatformData];
505 
506  if (libraryURI) {
507  FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library";
508  settings.advisory_script_entrypoint = entrypoint.UTF8String;
509  settings.advisory_script_uri = libraryURI.UTF8String;
510  } else if (entrypoint) {
511  settings.advisory_script_entrypoint = entrypoint.UTF8String;
512  settings.advisory_script_uri = std::string("main.dart");
513  } else {
514  settings.advisory_script_entrypoint = std::string("main");
515  settings.advisory_script_uri = std::string("main.dart");
516  }
517 
518  const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++];
519 
520  // The current thread will be used as the platform thread. Ensure that the message loop is
521  // initialized.
523 
524  uint32_t threadHostType = flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::GPU |
525  flutter::ThreadHost::Type::IO;
526  bool profilerEnabled = false;
527 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
528  (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
529  profilerEnabled = true;
530 #endif
531  if (profilerEnabled) {
532  threadHostType = threadHostType | flutter::ThreadHost::Type::Profiler;
533  }
534  _threadHost = {threadLabel.UTF8String, // label
535  threadHostType};
536 
537  // Lambda captures by pointers to ObjC objects are fine here because the
538  // create call is synchronous.
540  [self](flutter::Shell& shell) {
541  [self recreatePlatformViewController];
542  return std::make_unique<flutter::PlatformViewIOS>(
543  shell, self->_renderingApi, self->_surfaceFactory, shell.GetTaskRunners());
544  };
545 
547  [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
548 
549  flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
551  _threadHost.raster_thread->GetTaskRunner(), // raster
552  _threadHost.ui_thread->GetTaskRunner(), // ui
553  _threadHost.io_thread->GetTaskRunner() // io
554  );
555 
556  // Create the shell. This is a blocking operation.
557  _shell = flutter::Shell::Create(std::move(task_runners), // task runners
558  std::move(platformData), // window data
559  std::move(settings), // settings
560  on_create_platform_view, // platform view creation
561  on_create_rasterizer // rasterzier creation
562  );
563 
564  if (_shell == nullptr) {
565  FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: "
566  << entrypoint.UTF8String;
567  } else {
568  [self setupChannels];
569  [self onLocaleUpdated:nil];
570  [self initializeDisplays];
572  initWithEnableObservatoryPublication:settings.enable_observatory_publication]);
573  [self maybeSetupPlatformViewChannels];
574  _shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(_isGpuDisabled ? true : false);
575  if (profilerEnabled) {
576  [self startProfiler:threadLabel];
577  }
578  }
579 
580  return _shell != nullptr;
581 }
582 
583 - (void)initializeDisplays {
584  double refresh_rate = [[[DisplayLinkManager alloc] init] displayRefreshRate];
585  auto display = flutter::Display(refresh_rate);
586  _shell->OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, {display});
587 }
588 
589 - (BOOL)run {
590  return [self runWithEntrypoint:FlutterDefaultDartEntrypoint
591  libraryURI:nil
592  initialRoute:FlutterDefaultInitialRoute];
593 }
594 
595 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
596  return [self runWithEntrypoint:entrypoint
597  libraryURI:libraryURI
598  initialRoute:FlutterDefaultInitialRoute];
599 }
600 
601 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
602  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
603 }
604 
605 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
606  return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
607 }
608 
609 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
610  libraryURI:(NSString*)libraryURI
611  initialRoute:(NSString*)initialRoute {
612  if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
613  [self launchEngine:entrypoint libraryURI:libraryURI];
614  }
615 
616  return _shell != nullptr;
617 }
618 
619 - (void)notifyLowMemory {
620  if (_shell) {
621  _shell->NotifyLowMemoryWarning();
622  }
623  [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
624 }
625 
626 #pragma mark - Text input delegate
627 
628 - (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
629  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
630  arguments:@[ @(client), state ]];
631 }
632 
633 - (void)updateEditingClient:(int)client withState:(NSDictionary*)state withTag:(NSString*)tag {
634  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
635  arguments:@[ @(client), @{tag : state} ]];
636 }
637 
638 - (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state
639  withClient:(int)client
640  withPosition:(NSDictionary*)position {
641  NSString* stateString;
642  switch (state) {
643  case FlutterFloatingCursorDragStateStart:
644  stateString = @"FloatingCursorDragState.start";
645  break;
646  case FlutterFloatingCursorDragStateUpdate:
647  stateString = @"FloatingCursorDragState.update";
648  break;
649  case FlutterFloatingCursorDragStateEnd:
650  stateString = @"FloatingCursorDragState.end";
651  break;
652  }
653  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
654  arguments:@[ @(client), stateString, position ]];
655 }
656 
657 - (void)performAction:(FlutterTextInputAction)action withClient:(int)client {
658  NSString* actionString;
659  switch (action) {
660  case FlutterTextInputActionUnspecified:
661  // Where did the term "unspecified" come from? iOS has a "default" and Android
662  // has "unspecified." These 2 terms seem to mean the same thing but we need
663  // to pick just one. "unspecified" was chosen because "default" is often a
664  // reserved word in languages with switch statements (dart, java, etc).
665  actionString = @"TextInputAction.unspecified";
666  break;
667  case FlutterTextInputActionDone:
668  actionString = @"TextInputAction.done";
669  break;
670  case FlutterTextInputActionGo:
671  actionString = @"TextInputAction.go";
672  break;
673  case FlutterTextInputActionSend:
674  actionString = @"TextInputAction.send";
675  break;
676  case FlutterTextInputActionSearch:
677  actionString = @"TextInputAction.search";
678  break;
679  case FlutterTextInputActionNext:
680  actionString = @"TextInputAction.next";
681  break;
682  case FlutterTextInputActionContinue:
683  actionString = @"TextInputAction.continue";
684  break;
685  case FlutterTextInputActionJoin:
686  actionString = @"TextInputAction.join";
687  break;
688  case FlutterTextInputActionRoute:
689  actionString = @"TextInputAction.route";
690  break;
691  case FlutterTextInputActionEmergencyCall:
692  actionString = @"TextInputAction.emergencyCall";
693  break;
694  case FlutterTextInputActionNewline:
695  actionString = @"TextInputAction.newline";
696  break;
697  }
698  [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
699  arguments:@[ @(client), actionString ]];
700 }
701 
702 - (void)showAutocorrectionPromptRectForStart:(NSUInteger)start
703  end:(NSUInteger)end
704  withClient:(int)client {
705  [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
706  arguments:@[ @(client), @(start), @(end) ]];
707 }
708 
709 #pragma mark - Screenshot Delegate
710 
712  asBase64Encoded:(BOOL)base64Encode {
713  FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell";
714  return _shell->Screenshot(type, base64Encode);
715 }
716 
717 - (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
718  return _binaryMessenger;
719 }
720 
721 // For test only. Ideally we should create a dependency injector for all dependencies and
722 // remove this.
723 - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
724  // Discard the previous messenger and keep the new one.
725  _binaryMessenger.parent = nil;
726  [_binaryMessenger release];
728 }
729 
730 #pragma mark - FlutterBinaryMessenger
731 
732 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
733  [self sendOnChannel:channel message:message binaryReply:nil];
734 }
735 
736 - (void)sendOnChannel:(NSString*)channel
737  message:(NSData*)message
738  binaryReply:(FlutterBinaryReply)callback {
739  NSParameterAssert(channel);
740  NSAssert(_shell && _shell->IsSetup(),
741  @"Sending a message before the FlutterEngine has been run.");
743  (callback == nil) ? nullptr
744  : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
745  ^(NSData* reply) {
746  callback(reply);
747  },
748  _shell->GetTaskRunners().GetPlatformTaskRunner());
749  fml::RefPtr<flutter::PlatformMessage> platformMessage =
750  (message == nil) ? fml::MakeRefCounted<flutter::PlatformMessage>(channel.UTF8String, response)
751  : fml::MakeRefCounted<flutter::PlatformMessage>(
752  channel.UTF8String, flutter::GetVectorFromNSData(message), response);
753 
754  _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage);
755 }
756 
757 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
758  binaryMessageHandler:
759  (FlutterBinaryMessageHandler)handler {
760  NSParameterAssert(channel);
761  if (_shell && _shell->IsSetup()) {
762  self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
763  return _connections->AquireConnection(channel.UTF8String);
764  } else {
765  NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
766  // Setting a handler to nil for a not setup channel is a noop.
768  }
769 }
770 
771 - (void)cleanupConnection:(FlutterBinaryMessengerConnection)connection {
772  if (_shell && _shell->IsSetup()) {
773  std::string channel = _connections->CleanupConnection(connection);
774  if (!channel.empty()) {
775  self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.c_str(), nil);
776  }
777  }
778 }
779 
780 #pragma mark - FlutterTextureRegistry
781 
782 - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
783  int64_t textureId = _nextTextureId++;
784  self.iosPlatformView->RegisterExternalTexture(textureId, texture);
785  return textureId;
786 }
787 
788 - (void)unregisterTexture:(int64_t)textureId {
789  _shell->GetPlatformView()->UnregisterTexture(textureId);
790 }
791 
792 - (void)textureFrameAvailable:(int64_t)textureId {
793  _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
794 }
795 
796 - (NSString*)lookupKeyForAsset:(NSString*)asset {
797  return [FlutterDartProject lookupKeyForAsset:asset];
798 }
799 
800 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
801  return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
802 }
803 
804 - (id<FlutterPluginRegistry>)pluginRegistry {
805  return self;
806 }
807 
808 #pragma mark - FlutterPluginRegistry
809 
810 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
811  NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
812  self.pluginPublications[pluginKey] = [NSNull null];
813  FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
814  flutterEngine:self];
815  self.registrars[pluginKey] = result;
816  return [result autorelease];
817 }
818 
819 - (BOOL)hasPlugin:(NSString*)pluginKey {
820  return _pluginPublications[pluginKey] != nil;
821 }
822 
823 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
824  return _pluginPublications[pluginKey];
825 }
826 
827 #pragma mark - Notifications
828 
829 - (void)applicationBecameActive:(NSNotification*)notification {
830  [self setIsGpuDisabled:NO];
831 }
832 
833 - (void)applicationWillResignActive:(NSNotification*)notification {
834  [self setIsGpuDisabled:YES];
835 }
836 
837 - (void)applicationDidEnterBackground:(NSNotification*)notification {
838  [self notifyLowMemory];
839 }
840 
841 - (void)onMemoryWarning:(NSNotification*)notification {
842  [self notifyLowMemory];
843 }
844 
845 - (void)setIsGpuDisabled:(BOOL)value {
846  if (_shell) {
847  _shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(value ? true : false);
848  }
849  _isGpuDisabled = value;
850 }
851 
852 #pragma mark - Locale updates
853 
854 - (void)onLocaleUpdated:(NSNotification*)notification {
855  // [NSLocale currentLocale] provides an iOS resolved locale if the
856  // supported locales are exposed to the iOS embedder. Here, we get
857  // currentLocale and pass it to dart:ui
858  NSMutableArray<NSString*>* localeData = [[NSMutableArray new] autorelease];
859  NSLocale* platformResolvedLocale = [NSLocale currentLocale];
860  NSString* languageCode = [platformResolvedLocale objectForKey:NSLocaleLanguageCode];
861  NSString* countryCode = [platformResolvedLocale objectForKey:NSLocaleCountryCode];
862  NSString* scriptCode = [platformResolvedLocale objectForKey:NSLocaleScriptCode];
863  NSString* variantCode = [platformResolvedLocale objectForKey:NSLocaleVariantCode];
864  if (languageCode) {
865  [localeData addObject:languageCode];
866  [localeData addObject:(countryCode ? countryCode : @"")];
867  [localeData addObject:(scriptCode ? scriptCode : @"")];
868  [localeData addObject:(variantCode ? variantCode : @"")];
869  }
870  if (localeData.count != 0) {
871  [self.localizationChannel invokeMethod:@"setPlatformResolvedLocale" arguments:localeData];
872  }
873 
874  // Get and pass the user's preferred locale list to dart:ui
875  localeData = [[NSMutableArray new] autorelease];
876  NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
877  for (NSString* localeID in preferredLocales) {
878  NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
879  NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
880  NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
881  NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
882  NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
883  if (!languageCode) {
884  continue;
885  }
886  [localeData addObject:languageCode];
887  [localeData addObject:(countryCode ? countryCode : @"")];
888  [localeData addObject:(scriptCode ? scriptCode : @"")];
889  [localeData addObject:(variantCode ? variantCode : @"")];
890  }
891  if (localeData.count == 0) {
892  return;
893  }
894  [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
895 }
896 
897 - (void)waitForFirstFrame:(NSTimeInterval)timeout
898  callback:(void (^_Nonnull)(BOOL didTimeout))callback {
899  dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
900  dispatch_async(queue, ^{
901  fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
902  BOOL didTimeout =
903  self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
904  dispatch_async(dispatch_get_main_queue(), ^{
905  callback(didTimeout);
906  });
907  });
908 }
909 
910 @end
911 
912 @implementation FlutterEngineRegistrar {
913  NSString* _pluginKey;
914 }
915 
916 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
917  self = [super init];
918  NSAssert(self, @"Super init cannot be nil");
919  _pluginKey = [pluginKey copy];
921  return self;
922 }
923 
924 - (void)dealloc {
925  [_pluginKey release];
926  [super dealloc];
927 }
928 
929 - (NSObject<FlutterBinaryMessenger>*)messenger {
931 }
932 
933 - (NSObject<FlutterTextureRegistry>*)textures {
934  return _flutterEngine;
935 }
936 
937 - (void)publish:(NSObject*)value {
938  _flutterEngine.pluginPublications[_pluginKey] = value;
939 }
940 
941 - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
942  channel:(FlutterMethodChannel*)channel {
943  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
944  [delegate handleMethodCall:call result:result];
945  }];
946 }
947 
948 - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
949  id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
950  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
951  id<FlutterAppLifeCycleProvider> lifeCycleProvider =
952  (id<FlutterAppLifeCycleProvider>)appDelegate;
953  [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
954  }
955 }
956 
957 - (NSString*)lookupKeyForAsset:(NSString*)asset {
958  return [_flutterEngine lookupKeyForAsset:asset];
959 }
960 
961 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
962  return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
963 }
964 
965 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
966  withId:(NSString*)factoryId {
967  [self registerViewFactory:factory
968  withId:factoryId
969  gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
970 }
971 
972 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
973  withId:(NSString*)factoryId
974  gestureRecognizersBlockingPolicy:
975  (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
976  [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
977  gestureRecognizersBlockingPolicy);
978 }
979 
980 @end
std::unique_ptr< fml::Thread > ui_thread
Definition: thread_host.h:26
BOOL forceSoftwareRendering
Definition: FlutterView.h:37
fml::scoped_nsobject< FlutterTextInputPlugin > _textInputPlugin
BOOL _allowHeadlessExecution
FlutterTextInputPlugin * textInputPlugin
Maintains a current integer assigned to a name (connections).
void handleMethodCall:result:(FlutterMethodCall *call, [result] FlutterResult result)
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition: shell.h:99
std::unique_ptr< fml::WeakPtrFactory< FlutterEngine > > _weakFactory
NSObject< FlutterBinaryMessenger > * binaryMessenger
#define FML_DCHECK(condition)
Definition: logging.h:86
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
static std::shared_ptr< IOSSurfaceFactory > Create(IOSRenderingAPI rendering_api)
std::unique_ptr< flutter::Shell > _shell
fml::scoped_nsobject< FlutterBasicMessageChannel > _keyEventChannel
std::unique_ptr< flutter::ConnectionCollection > _connections
fml::scoped_nsobject< FlutterPlatformPlugin > _platformPlugin
std::unique_ptr< fml::Thread > profiler_thread
Definition: thread_host.h:29
void reset(NST object=nil)
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
static void EnsureInitializedForCurrentThread()
Definition: message_loop.cc:27
NSString * isolateId
int64_t _nextTextureId
#define FML_LOG(severity)
Definition: logging.h:65
fml::scoped_nsobject< FlutterMethodChannel > _platformViewsChannel
std::vector< uint8_t > GetVectorFromNSData(NSData *data)
fml::scoped_nsobject< FlutterObservatoryPublisher > _publisher
fml::scoped_nsobject< FlutterMethodChannel > _localizationChannel
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition: message_loop.cc:56
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
instancetype sharedInstance()
std::shared_ptr< flutter::FlutterPlatformViewsController > _platformViewsController
A POD type used to return the screenshot data along with the size of the frame.
Definition: rasterizer.h:279
uint8_t value
ScreenshotType
The type of the screenshot to obtain of the previously rendered layer tree.
Definition: rasterizer.h:249
static constexpr int kNumProfilerSamplesPerSec
flutter::ThreadHost _threadHost
SemanticsAction action
size_t length
The collection of all the threads used by the engine.
Definition: thread_host.h:16
NSString *const FlutterDefaultDartEntrypoint
instancetype initWithName:project:allowHeadlessExecution:(NSString *labelPrefix, [project] nullable FlutterDartProject *project, [allowHeadlessExecution] BOOL NS_DESIGNATED_INITIALIZER)
NSString * lookupKeyForAsset:fromPackage:(NSString *asset, [fromPackage] NSString *package)
fml::scoped_nsobject< FlutterMethodChannel > _platformChannel
bool EnableTracingIfNecessary(const Settings &vm_settings)
Enables tracing in the process so that JIT mode VMs may be launched. Explicitly enabling tracing is n...
Definition: ptrace_check.h:45
NSString * lookupKeyForAsset:(NSString *asset)
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
FlutterPlatformViewGestureRecognizersBlockingPolicy
fml::WeakPtr< FlutterViewController > _viewController
NSString * _labelPrefix
std::shared_ptr< flutter::IOSSurfaceFactory > _surfaceFactory
NSString *const FlutterDefaultInitialRoute
std::unique_ptr< fml::Thread > io_thread
Definition: thread_host.h:28
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
void sendMessage:(id _Nullable message)
std::unique_ptr< flutter::ProfilerMetricsIOS > _profiler_metrics
fml::scoped_nsobject< FlutterMethodChannel > _textInputChannel
static Connection MakeErrorConnection(int errCode)
void handleMethodCall:result:(FlutterMethodCall *call, [result] FlutterResult result)
FlutterBasicMessageChannel * lifecycleChannel
int64_t FlutterBinaryMessengerConnection
fml::scoped_nsobject< FlutterBasicMessageChannel > _lifecycleChannel
flutter::IOSRenderingAPI _renderingApi
static std::unique_ptr< Shell > Create(TaskRunners task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition: shell.cc:239
void SetOwnerViewController(fml::WeakPtr< FlutterViewController > owner_controller)
std::unique_ptr< fml::Thread > raster_thread
Definition: thread_host.h:27
FlutterBinaryMessengerRelay * _binaryMessenger
fml::scoped_nsobject< FlutterBasicMessageChannel > _systemChannel
NSObject< FlutterBinaryMessenger > * parent
fml::scoped_nsobject< FlutterMethodChannel > _navigationChannel
std::unique_ptr< flutter::SamplingProfiler > _profiler
FlutterViewController * viewController
FlutterEngine * flutterEngine
FlutterEngine * _flutterEngine
fml::scoped_nsobject< FlutterBasicMessageChannel > _settingsChannel