Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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/common/constants.h"
12#include "flutter/fml/message_loop.h"
13#include "flutter/fml/platform/darwin/platform_version.h"
14#include "flutter/fml/platform/darwin/weak_nsobject.h"
15#include "flutter/fml/trace_event.h"
16#include "flutter/runtime/ptrace_check.h"
17#include "flutter/shell/common/engine.h"
18#include "flutter/shell/common/platform_view.h"
19#include "flutter/shell/common/shell.h"
20#include "flutter/shell/common/switches.h"
21#include "flutter/shell/common/thread_host.h"
22#include "flutter/shell/common/variable_refresh_rate_display.h"
23#import "flutter/shell/platform/darwin/common/command_line.h"
24#import "flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h"
25#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
26#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartVMServicePublisher.h"
27#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterIndirectScribbleDelegate.h"
28#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
29#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSpellCheckPlugin.h"
30#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
31#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.h"
32#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h"
33#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h"
34#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
35#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
36#import "flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h"
37#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
38#import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
39#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
40#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
41#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
42#include "flutter/shell/profiling/sampling_profiler.h"
43
44/// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities
45/// Using iOS platform thread API to configure thread priority
47 // set thread name
49
50 // set thread priority
51 switch (config.priority) {
53 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
54 [[NSThread currentThread] setThreadPriority:0];
55 break;
56 }
58 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
59 [[NSThread currentThread] setThreadPriority:0.5];
60 break;
61 }
64 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
65 [[NSThread currentThread] setThreadPriority:1.0];
66 sched_param param;
67 int policy;
68 pthread_t thread = pthread_self();
69 if (!pthread_getschedparam(thread, &policy, &param)) {
70 param.sched_priority = 50;
71 pthread_setschedparam(thread, policy, &param);
72 }
73 break;
74 }
75 }
76}
77
78#pragma mark - Public exported constants
79
80NSString* const FlutterDefaultDartEntrypoint = nil;
81NSString* const FlutterDefaultInitialRoute = nil;
82
83#pragma mark - Internal constants
84
85NSString* const kFlutterEngineWillDealloc = @"FlutterEngineWillDealloc";
86NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
87static constexpr int kNumProfilerSamplesPerSec = 5;
88
90@property(nonatomic, assign) FlutterEngine* flutterEngine;
91- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine;
92@end
93
94@interface FlutterEngine () <FlutterIndirectScribbleDelegate,
95 FlutterUndoManagerDelegate,
96 FlutterTextInputDelegate,
97 FlutterBinaryMessenger,
98 FlutterTextureRegistry>
99// Maintains a dictionary of plugin names that have registered with the engine. Used by
100// FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
101@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
102@property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
103
104@property(nonatomic, readwrite, copy) NSString* isolateId;
105@property(nonatomic, copy) NSString* initialRoute;
106@property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
107
108#pragma mark - Embedder API properties
109
110@property(nonatomic, assign) BOOL enableEmbedderAPI;
111// Function pointers for interacting with the embedder.h API.
112@property(nonatomic) FlutterEngineProcTable& embedderAPI;
113@end
114
115@implementation FlutterEngine {
117 std::shared_ptr<flutter::ThreadHost> _threadHost;
118 std::unique_ptr<flutter::Shell> _shell;
119 NSString* _labelPrefix;
120 std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory;
121
124
125 std::shared_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
127 std::shared_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
128 std::shared_ptr<flutter::SamplingProfiler> _profiler;
129
130 // Channels
150
152
157 std::unique_ptr<flutter::ConnectionCollection> _connections;
158}
159
160- (instancetype)init {
161 return [self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
162}
163
164- (instancetype)initWithName:(NSString*)labelPrefix {
165 return [self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
166}
167
168- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
169 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
170}
171
172- (instancetype)initWithName:(NSString*)labelPrefix
173 project:(FlutterDartProject*)project
174 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
175 return [self initWithName:labelPrefix
176 project:project
177 allowHeadlessExecution:allowHeadlessExecution
178 restorationEnabled:NO];
179}
180
181- (instancetype)initWithName:(NSString*)labelPrefix
182 project:(FlutterDartProject*)project
183 allowHeadlessExecution:(BOOL)allowHeadlessExecution
184 restorationEnabled:(BOOL)restorationEnabled {
185 self = [super init];
186 NSAssert(self, @"Super init cannot be nil");
187 NSAssert(labelPrefix, @"labelPrefix is required");
188
189 _restorationEnabled = restorationEnabled;
190 _allowHeadlessExecution = allowHeadlessExecution;
191 _labelPrefix = [labelPrefix copy];
192
193 _weakFactory = std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(self);
194
195 if (project == nil) {
196 _dartProject.reset([[FlutterDartProject alloc] init]);
197 } else {
198 _dartProject.reset([project retain]);
199 }
200
201 _enableEmbedderAPI = _dartProject.get().settings.enable_embedder_api;
202 if (_enableEmbedderAPI) {
203 NSLog(@"============== iOS: enable_embedder_api is on ==============");
204 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
205 FlutterEngineGetProcAddresses(&_embedderAPI);
206 }
207
208 if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
209 NSLog(
210 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
211 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
212 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
213 @"profile and release mode apps can be launched from the home screen.");
214 [self release];
215 return nil;
216 }
217
218 _pluginPublications = [[NSMutableDictionary alloc] init];
219 _registrars = [[NSMutableDictionary alloc] init];
220 [self recreatePlatformViewController];
221
222 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
223 _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
225
226 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
227 [center addObserver:self
228 selector:@selector(onMemoryWarning:)
229 name:UIApplicationDidReceiveMemoryWarningNotification
230 object:nil];
231
232#if APPLICATION_EXTENSION_API_ONLY
233 if (@available(iOS 13.0, *)) {
234 [self setUpSceneLifecycleNotifications:center];
235 } else {
236 [self setUpApplicationLifecycleNotifications:center];
237 }
238#else
239 [self setUpApplicationLifecycleNotifications:center];
240#endif
241
242 [center addObserver:self
243 selector:@selector(onLocaleUpdated:)
244 name:NSCurrentLocaleDidChangeNotification
245 object:nil];
246
247 return self;
248}
249
250- (void)setUpSceneLifecycleNotifications:(NSNotificationCenter*)center API_AVAILABLE(ios(13.0)) {
251 [center addObserver:self
252 selector:@selector(sceneWillEnterForeground:)
253 name:UISceneWillEnterForegroundNotification
254 object:nil];
255 [center addObserver:self
256 selector:@selector(sceneDidEnterBackground:)
257 name:UISceneDidEnterBackgroundNotification
258 object:nil];
259}
260
261- (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center {
262 [center addObserver:self
263 selector:@selector(applicationWillEnterForeground:)
264 name:UIApplicationWillEnterForegroundNotification
265 object:nil];
266 [center addObserver:self
267 selector:@selector(applicationDidEnterBackground:)
268 name:UIApplicationDidEnterBackgroundNotification
269 object:nil];
270}
271
272- (void)recreatePlatformViewController {
275}
276
277- (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
278 return _renderingApi;
279}
280
281- (void)dealloc {
282 /// Notify plugins of dealloc. This should happen first in dealloc since the
283 /// plugins may be talking to things like the binaryMessenger.
284 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
285 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
286 NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
287 [object detachFromEngineForRegistrar:registrar];
288 }
289 }];
290
291 [[NSNotificationCenter defaultCenter] postNotificationName:kFlutterEngineWillDealloc
292 object:self
293 userInfo:nil];
294
295 // It will be destroyed and invalidate its weak pointers
296 // before any other members are destroyed.
297 _weakFactory.reset();
298
299 /// nil out weak references.
300 [_registrars
301 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
302 registrar.flutterEngine = nil;
303 }];
304
305 [_labelPrefix release];
306 [_initialRoute release];
307 [_pluginPublications release];
308 [_registrars release];
311 [_binaryMessenger release];
312 [_textureRegistry release];
313 _textureRegistry = nil;
314 [_isolateId release];
315
316 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
317 if (_flutterViewControllerWillDeallocObserver) {
318 [center removeObserver:_flutterViewControllerWillDeallocObserver];
319 [_flutterViewControllerWillDeallocObserver release];
320 }
321 [center removeObserver:self];
322
323 [super dealloc];
324}
325
326- (flutter::Shell&)shell {
328 return *_shell;
329}
330
331- (fml::WeakNSObject<FlutterEngine>)getWeakNSObject {
332 return _weakFactory->GetWeakNSObject();
333}
334
335- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
336 if (!self.platformView) {
337 return;
338 }
339 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
340}
341
342- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
343 if (!self.platformView) {
344 return;
345 }
346 self.platformView->DispatchPointerDataPacket(std::move(packet));
347}
348
349- (fml::WeakPtr<flutter::PlatformView>)platformView {
351 return _shell->GetPlatformView();
352}
353
354- (flutter::PlatformViewIOS*)iosPlatformView {
356 return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get());
357}
358
359- (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
361 return _shell->GetTaskRunners().GetPlatformTaskRunner();
362}
363
364- (fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
366 return _shell->GetTaskRunners().GetUITaskRunner();
367}
368
369- (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
371 return _shell->GetTaskRunners().GetRasterTaskRunner();
372}
373
374- (void)sendKeyEvent:(const FlutterKeyEvent&)event
375 callback:(FlutterKeyEventCallback)callback
376 userData:(void*)userData API_AVAILABLE(ios(13.4)) {
377 if (@available(iOS 13.4, *)) {
378 } else {
379 return;
380 }
381 if (!self.platformView) {
382 return;
383 }
384 const char* character = event.character;
385
386 flutter::KeyData key_data;
387 key_data.Clear();
388 key_data.timestamp = (uint64_t)event.timestamp;
389 switch (event.type) {
392 break;
395 break;
398 break;
399 }
400 key_data.physical = event.physical;
401 key_data.logical = event.logical;
402 key_data.synthesized = event.synthesized;
403
404 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
405 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
406
407 auto response = ^(NSData* reply) {
408 if (callback == nullptr) {
409 return;
410 }
411 BOOL handled = FALSE;
412 if (reply.length == 1 && *reinterpret_cast<const uint8_t*>(reply.bytes) == 1) {
413 handled = TRUE;
414 }
415 callback(handled, userData);
416 };
417
418 [self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
419}
420
421- (void)ensureSemanticsEnabled {
422 self.iosPlatformView->SetSemanticsEnabled(true);
423}
424
425- (void)setViewController:(FlutterViewController*)viewController {
426 FML_DCHECK(self.iosPlatformView);
429 self.iosPlatformView->SetOwnerViewController(_viewController);
430 [self maybeSetupPlatformViewChannels];
431 [self updateDisplays];
432 _textInputPlugin.get().viewController = viewController;
433
434 if (viewController) {
435 __block FlutterEngine* blockSelf = self;
437 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
438 object:viewController
439 queue:[NSOperationQueue mainQueue]
440 usingBlock:^(NSNotification* note) {
441 [blockSelf notifyViewControllerDeallocated];
442 }];
443 } else {
444 self.flutterViewControllerWillDeallocObserver = nil;
445 [self notifyLowMemory];
446 }
447}
448
449- (void)attachView {
450 self.iosPlatformView->attachView();
451}
452
453- (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
454 if (observer != _flutterViewControllerWillDeallocObserver) {
455 if (_flutterViewControllerWillDeallocObserver) {
456 [[NSNotificationCenter defaultCenter]
457 removeObserver:_flutterViewControllerWillDeallocObserver];
458 [_flutterViewControllerWillDeallocObserver release];
459 }
460 _flutterViewControllerWillDeallocObserver = [observer retain];
461 }
462}
463
464- (void)notifyViewControllerDeallocated {
465 [[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
466 _textInputPlugin.get().viewController = nil;
468 [self destroyContext];
469 } else if (_shell) {
470 flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
471 if (platform_view) {
473 }
474 }
475 [_textInputPlugin.get() resetViewResponder];
476 _viewController.reset();
477}
478
479- (void)destroyContext {
480 [self resetChannels];
481 self.isolateId = nil;
482 _shell.reset();
483 _profiler.reset();
484 _threadHost.reset();
486}
487
489 if (!_viewController) {
490 return nil;
491 }
492 return _viewController.get();
493}
494
495- (FlutterPlatformPlugin*)platformPlugin {
496 return _platformPlugin.get();
497}
498- (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController {
500}
502 return _textInputPlugin.get();
503}
504- (FlutterUndoManagerPlugin*)undoManagerPlugin {
505 return _undoManagerPlugin.get();
506}
507- (FlutterRestorationPlugin*)restorationPlugin {
508 return _restorationPlugin.get();
509}
510- (FlutterMethodChannel*)localizationChannel {
511 return _localizationChannel.get();
512}
513- (FlutterMethodChannel*)navigationChannel {
514 return _navigationChannel.get();
515}
516- (FlutterMethodChannel*)restorationChannel {
517 return _restorationChannel.get();
518}
519- (FlutterMethodChannel*)platformChannel {
520 return _platformChannel.get();
521}
522- (FlutterMethodChannel*)textInputChannel {
523 return _textInputChannel.get();
524}
525- (FlutterMethodChannel*)undoManagerChannel {
526 return _undoManagerChannel.get();
527}
528- (FlutterMethodChannel*)scribbleChannel {
529 return _scribbleChannel.get();
530}
531- (FlutterMethodChannel*)spellCheckChannel {
532 return _spellCheckChannel.get();
533}
534- (FlutterBasicMessageChannel*)lifecycleChannel {
535 return _lifecycleChannel.get();
536}
537- (FlutterBasicMessageChannel*)systemChannel {
538 return _systemChannel.get();
539}
540- (FlutterBasicMessageChannel*)settingsChannel {
541 return _settingsChannel.get();
542}
543- (FlutterBasicMessageChannel*)keyEventChannel {
544 return _keyEventChannel.get();
545}
546
547- (NSURL*)observatoryUrl {
548 return [_publisher.get() url];
549}
550
551- (NSURL*)vmServiceUrl {
552 return [_publisher.get() url];
553}
554
555- (void)resetChannels {
564 _lifecycleChannel.reset();
565 _systemChannel.reset();
566 _settingsChannel.reset();
567 _keyEventChannel.reset();
569}
570
571- (void)startProfiler {
572 FML_DCHECK(!_threadHost->name_prefix.empty());
573 _profiler_metrics = std::make_shared<flutter::ProfilerMetricsIOS>();
574 _profiler = std::make_shared<flutter::SamplingProfiler>(
575 _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
576 [self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
577 _profiler->Start();
578}
579
580// If you add a channel, be sure to also update `resetChannels`.
581// Channels get a reference to the engine, and therefore need manual
582// cleanup for proper collection.
583- (void)setUpChannels {
584 // This will be invoked once the shell is done setting up and the isolate ID
585 // for the UI isolate is available.
586 fml::WeakNSObject<FlutterEngine> weakSelf = [self getWeakNSObject];
587 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
588 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
589 if (weakSelf) {
590 weakSelf.get().isolateId =
591 [[FlutterStringCodec sharedInstance] decode:message];
592 }
593 }];
594
596 initWithName:@"flutter/localization"
597 binaryMessenger:self.binaryMessenger
598 codec:[FlutterJSONMethodCodec sharedInstance]]);
599
601 initWithName:@"flutter/navigation"
602 binaryMessenger:self.binaryMessenger
603 codec:[FlutterJSONMethodCodec sharedInstance]]);
604
605 if ([_initialRoute length] > 0) {
606 // Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
607 [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
608 [_initialRoute release];
609 _initialRoute = nil;
610 }
611
613 initWithName:@"flutter/restoration"
614 binaryMessenger:self.binaryMessenger
615 codec:[FlutterStandardMethodCodec sharedInstance]]);
616
618 initWithName:@"flutter/platform"
619 binaryMessenger:self.binaryMessenger
620 codec:[FlutterJSONMethodCodec sharedInstance]]);
621
623 initWithName:@"flutter/platform_views"
624 binaryMessenger:self.binaryMessenger
625 codec:[FlutterStandardMethodCodec sharedInstance]]);
626
628 initWithName:@"flutter/textinput"
629 binaryMessenger:self.binaryMessenger
630 codec:[FlutterJSONMethodCodec sharedInstance]]);
631
633 initWithName:@"flutter/undomanager"
634 binaryMessenger:self.binaryMessenger
635 codec:[FlutterJSONMethodCodec sharedInstance]]);
636
638 initWithName:@"flutter/scribble"
639 binaryMessenger:self.binaryMessenger
640 codec:[FlutterJSONMethodCodec sharedInstance]]);
641
643 initWithName:@"flutter/spellcheck"
644 binaryMessenger:self.binaryMessenger
645 codec:[FlutterStandardMethodCodec sharedInstance]]);
646
648 initWithName:@"flutter/lifecycle"
649 binaryMessenger:self.binaryMessenger
650 codec:[FlutterStringCodec sharedInstance]]);
651
653 initWithName:@"flutter/system"
654 binaryMessenger:self.binaryMessenger
655 codec:[FlutterJSONMessageCodec sharedInstance]]);
656
658 initWithName:@"flutter/settings"
659 binaryMessenger:self.binaryMessenger
660 codec:[FlutterJSONMessageCodec sharedInstance]]);
661
663 initWithName:@"flutter/keyevent"
664 binaryMessenger:self.binaryMessenger
665 codec:[FlutterJSONMessageCodec sharedInstance]]);
666
667 FlutterTextInputPlugin* textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
670 [textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
671
672 FlutterUndoManagerPlugin* undoManagerPlugin =
673 [[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
674 _undoManagerPlugin.reset(undoManagerPlugin);
675
676 _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakNSObject]]);
677
679 initWithChannel:_restorationChannel.get()
680 restorationEnabled:_restorationEnabled]);
681 _spellCheckPlugin.reset([[FlutterSpellCheckPlugin alloc] init]);
682
684 initWithName:@"flutter/screenshot"
685 binaryMessenger:self.binaryMessenger
686 codec:[FlutterStandardMethodCodec sharedInstance]]);
687
688 [_screenshotChannel.get()
689 setMethodCallHandler:^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
690 if (!(weakSelf.get() && weakSelf.get()->_shell && weakSelf.get()->_shell->IsSetup())) {
691 return result([FlutterError
692 errorWithCode:@"invalid_state"
693 message:@"Requesting screenshot while engine is not running."
694 details:nil]);
695 }
696 flutter::Rasterizer::Screenshot screenshot =
697 [weakSelf.get() screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData
698 base64Encode:NO];
699 if (!screenshot.data) {
700 return result([FlutterError errorWithCode:@"failure"
701 message:@"Unable to get screenshot."
702 details:nil]);
703 }
704 // TODO(gaaclarke): Find way to eliminate this data copy.
705 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
706 length:screenshot.data->size()];
707 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
708 NSNumber* width = @(screenshot.frame_size.fWidth);
709 NSNumber* height = @(screenshot.frame_size.fHeight);
710 return result(@[ width, height, format ?: [NSNull null], data ]);
711 }];
712}
713
714- (void)maybeSetupPlatformViewChannels {
715 if (_shell && self.shell.IsSetup()) {
716 FlutterPlatformPlugin* platformPlugin = _platformPlugin.get();
717 [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
718 [platformPlugin handleMethodCall:call result:result];
719 }];
720
721 fml::WeakNSObject<FlutterEngine> weakSelf = [self getWeakNSObject];
722 [_platformViewsChannel.get()
723 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
724 if (weakSelf) {
725 weakSelf.get().platformViewsController->OnMethodCall(call, result);
726 }
727 }];
728
730 [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
731 [textInputPlugin handleMethodCall:call result:result];
732 }];
733
734 FlutterUndoManagerPlugin* undoManagerPlugin = _undoManagerPlugin.get();
735 [_undoManagerChannel.get()
736 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
737 [undoManagerPlugin handleMethodCall:call result:result];
738 }];
739
740 FlutterSpellCheckPlugin* spellCheckPlugin = _spellCheckPlugin.get();
741 [_spellCheckChannel.get()
742 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
743 [spellCheckPlugin handleMethodCall:call result:result];
744 }];
745 }
746}
747
748- (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type
749 base64Encode:(bool)base64Encode {
750 return self.shell.Screenshot(type, base64Encode);
751}
752
753- (void)launchEngine:(NSString*)entrypoint
754 libraryURI:(NSString*)libraryOrNil
755 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
756 // Launch the Dart application with the inferred run configuration.
757 self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
758 libraryOrNil:libraryOrNil
759 entrypointArgs:entrypointArgs]);
760}
761
762- (void)setUpShell:(std::unique_ptr<flutter::Shell>)shell
763 withVMServicePublication:(BOOL)doesVMServicePublication {
764 _shell = std::move(shell);
765 [self setUpChannels];
766 [self onLocaleUpdated:nil];
767 [self updateDisplays];
769 initWithEnableVMServicePublication:doesVMServicePublication]);
770 [self maybeSetupPlatformViewChannels];
771 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
773}
774
775+ (BOOL)isProfilerEnabled {
776 bool profilerEnabled = false;
777#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
778 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
779 profilerEnabled = true;
780#endif
781 return profilerEnabled;
782}
783
784+ (NSString*)generateThreadLabel:(NSString*)labelPrefix {
785 static size_t s_shellCount = 0;
786 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
787}
788
789+ (flutter::ThreadHost)makeThreadHost:(NSString*)threadLabel {
790 // The current thread will be used as the platform thread. Ensure that the message loop is
791 // initialized.
793
796
797 if ([FlutterEngine isProfilerEnabled]) {
798 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
799 }
800
801 flutter::ThreadHost::ThreadHostConfig host_config(threadLabel.UTF8String, threadHostType,
803
804 host_config.ui_config =
806 flutter::ThreadHost::Type::kUi, threadLabel.UTF8String),
808
809 host_config.raster_config =
811 flutter::ThreadHost::Type::kRaster, threadLabel.UTF8String),
813
814 host_config.io_config =
816 flutter::ThreadHost::Type::kIo, threadLabel.UTF8String),
818
819 return (flutter::ThreadHost){host_config};
820}
821
822static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
823 if (libraryURI) {
824 FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library";
825 settings->advisory_script_entrypoint = entrypoint.UTF8String;
826 settings->advisory_script_uri = libraryURI.UTF8String;
827 } else if (entrypoint) {
828 settings->advisory_script_entrypoint = entrypoint.UTF8String;
829 settings->advisory_script_uri = std::string("main.dart");
830 } else {
831 settings->advisory_script_entrypoint = std::string("main");
832 settings->advisory_script_uri = std::string("main.dart");
833 }
834}
835
836- (BOOL)createShell:(NSString*)entrypoint
837 libraryURI:(NSString*)libraryURI
838 initialRoute:(NSString*)initialRoute {
839 if (_shell != nullptr) {
840 FML_LOG(WARNING) << "This FlutterEngine was already invoked.";
841 return NO;
842 }
843
844 self.initialRoute = initialRoute;
845
846 auto settings = [_dartProject.get() settings];
847 if (initialRoute != nil) {
848 self.initialRoute = initialRoute;
849 } else if (settings.route.empty() == false) {
850 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
851 }
852
853 FlutterView.forceSoftwareRendering = settings.enable_software_rendering;
854
855 auto platformData = [_dartProject.get() defaultPlatformData];
856
857 SetEntryPoint(&settings, entrypoint, libraryURI);
858
859 NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix];
860 _threadHost = std::make_shared<flutter::ThreadHost>();
862
863 // Lambda captures by pointers to ObjC objects are fine here because the
864 // create call is synchronous.
866 [self](flutter::Shell& shell) {
867 [self recreatePlatformViewController];
868 return std::make_unique<flutter::PlatformViewIOS>(
869 shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners(),
870 shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch());
871 };
872
874 [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
875
876 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
878 _threadHost->raster_thread->GetTaskRunner(), // raster
879 _threadHost->ui_thread->GetTaskRunner(), // ui
880 _threadHost->io_thread->GetTaskRunner() // io
881 );
882
883#if APPLICATION_EXTENSION_API_ONLY
884 if (@available(iOS 13.0, *)) {
885 _isGpuDisabled = self.viewController.flutterWindowSceneIfViewLoaded.activationState ==
886 UISceneActivationStateBackground;
887 } else {
888 // [UIApplication sharedApplication API is not available for app extension.
889 // We intialize the shell assuming the GPU is required.
890 _isGpuDisabled = NO;
891 }
892#else
893 _isGpuDisabled =
894 [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
895#endif
896
897 // Create the shell. This is a blocking operation.
898 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
899 /*platform_data=*/platformData,
900 /*task_runners=*/task_runners,
901 /*settings=*/settings,
902 /*on_create_platform_view=*/on_create_platform_view,
903 /*on_create_rasterizer=*/on_create_rasterizer,
904 /*is_gpu_disabled=*/_isGpuDisabled);
905
906 if (shell == nullptr) {
907 FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: "
908 << entrypoint.UTF8String;
909 } else {
910 // TODO(vashworth): Remove once done debugging https://github.com/flutter/flutter/issues/129836
911 FML_LOG(INFO) << "Enabled VM Service Publication: " << settings.enable_vm_service_publication;
912 [self setUpShell:std::move(shell)
913 withVMServicePublication:settings.enable_vm_service_publication];
914 if ([FlutterEngine isProfilerEnabled]) {
915 [self startProfiler];
916 }
917 }
918
919 return _shell != nullptr;
920}
921
922- (void)updateDisplays {
923 if (!_shell) {
924 // Tests may do this.
925 return;
926 }
927 auto vsync_waiter = _shell->GetVsyncWaiter().lock();
928 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
929 std::vector<std::unique_ptr<flutter::Display>> displays;
930 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
931 auto scale = UIScreen.mainScreen.scale;
932 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
933 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
934 _shell->OnDisplayUpdates(std::move(displays));
935}
936
937- (BOOL)run {
938 return [self runWithEntrypoint:FlutterDefaultDartEntrypoint
939 libraryURI:nil
940 initialRoute:FlutterDefaultInitialRoute];
941}
942
943- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
944 return [self runWithEntrypoint:entrypoint
945 libraryURI:libraryURI
946 initialRoute:FlutterDefaultInitialRoute];
947}
948
949- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
950 return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
951}
952
953- (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
954 return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
955}
956
957- (BOOL)runWithEntrypoint:(NSString*)entrypoint
958 libraryURI:(NSString*)libraryURI
959 initialRoute:(NSString*)initialRoute {
960 return [self runWithEntrypoint:entrypoint
961 libraryURI:libraryURI
962 initialRoute:initialRoute
963 entrypointArgs:nil];
964}
965
966- (BOOL)runWithEntrypoint:(NSString*)entrypoint
967 libraryURI:(NSString*)libraryURI
968 initialRoute:(NSString*)initialRoute
969 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
970 if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
971 [self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
972 }
973
974 return _shell != nullptr;
975}
976
977- (void)notifyLowMemory {
978 if (_shell) {
979 _shell->NotifyLowMemoryWarning();
980 }
981 [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
982}
983
984#pragma mark - Text input delegate
985
986- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
987 updateEditingClient:(int)client
988 withState:(NSDictionary*)state {
989 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
990 arguments:@[ @(client), state ]];
991}
992
993- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
994 updateEditingClient:(int)client
995 withState:(NSDictionary*)state
996 withTag:(NSString*)tag {
997 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
998 arguments:@[ @(client), @{tag : state} ]];
999}
1000
1001- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1002 updateEditingClient:(int)client
1003 withDelta:(NSDictionary*)delta {
1004 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
1005 arguments:@[ @(client), delta ]];
1006}
1007
1008- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1009 updateFloatingCursor:(FlutterFloatingCursorDragState)state
1010 withClient:(int)client
1011 withPosition:(NSDictionary*)position {
1012 NSString* stateString;
1013 switch (state) {
1014 case FlutterFloatingCursorDragStateStart:
1015 stateString = @"FloatingCursorDragState.start";
1016 break;
1017 case FlutterFloatingCursorDragStateUpdate:
1018 stateString = @"FloatingCursorDragState.update";
1019 break;
1020 case FlutterFloatingCursorDragStateEnd:
1021 stateString = @"FloatingCursorDragState.end";
1022 break;
1023 }
1024 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
1025 arguments:@[ @(client), stateString, position ]];
1026}
1027
1028- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1029 performAction:(FlutterTextInputAction)action
1030 withClient:(int)client {
1031 NSString* actionString;
1032 switch (action) {
1033 case FlutterTextInputActionUnspecified:
1034 // Where did the term "unspecified" come from? iOS has a "default" and Android
1035 // has "unspecified." These 2 terms seem to mean the same thing but we need
1036 // to pick just one. "unspecified" was chosen because "default" is often a
1037 // reserved word in languages with switch statements (dart, java, etc).
1038 actionString = @"TextInputAction.unspecified";
1039 break;
1040 case FlutterTextInputActionDone:
1041 actionString = @"TextInputAction.done";
1042 break;
1043 case FlutterTextInputActionGo:
1044 actionString = @"TextInputAction.go";
1045 break;
1046 case FlutterTextInputActionSend:
1047 actionString = @"TextInputAction.send";
1048 break;
1049 case FlutterTextInputActionSearch:
1050 actionString = @"TextInputAction.search";
1051 break;
1052 case FlutterTextInputActionNext:
1053 actionString = @"TextInputAction.next";
1054 break;
1055 case FlutterTextInputActionContinue:
1056 actionString = @"TextInputAction.continueAction";
1057 break;
1058 case FlutterTextInputActionJoin:
1059 actionString = @"TextInputAction.join";
1060 break;
1061 case FlutterTextInputActionRoute:
1062 actionString = @"TextInputAction.route";
1063 break;
1064 case FlutterTextInputActionEmergencyCall:
1065 actionString = @"TextInputAction.emergencyCall";
1066 break;
1067 case FlutterTextInputActionNewline:
1068 actionString = @"TextInputAction.newline";
1069 break;
1070 }
1071 [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
1072 arguments:@[ @(client), actionString ]];
1073}
1074
1075- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1076 showAutocorrectionPromptRectForStart:(NSUInteger)start
1077 end:(NSUInteger)end
1078 withClient:(int)client {
1079 [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1080 arguments:@[ @(client), @(start), @(end) ]];
1081}
1082
1083- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1084 willDismissEditMenuWithTextInputClient:(int)client {
1085 [_platformChannel.get() invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1086 arguments:@[ @(client) ]];
1087}
1088
1089#pragma mark - FlutterViewEngineDelegate
1090
1091- (void)flutterTextInputView:(FlutterTextInputView*)textInputView showToolbar:(int)client {
1092 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1093 // the framework has finished transitioning to the Scribble channel.
1094 // https://github.com/flutter/flutter/pull/115296
1095 [_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1096}
1097
1098- (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1099 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1100 atPoint:(CGPoint)referencePoint
1101 result:(FlutterResult)callback {
1102 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1103 // the framework has finished transitioning to the Scribble channel.
1104 // https://github.com/flutter/flutter/pull/115296
1105 [_textInputChannel.get()
1106 invokeMethod:@"TextInputClient.focusElement"
1107 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1108 result:callback];
1109}
1110
1111- (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1112 requestElementsInRect:(CGRect)rect
1113 result:(FlutterResult)callback {
1114 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1115 // the framework has finished transitioning to the Scribble channel.
1116 // https://github.com/flutter/flutter/pull/115296
1117 [_textInputChannel.get()
1118 invokeMethod:@"TextInputClient.requestElementsInRect"
1119 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1120 result:callback];
1121}
1122
1123- (void)flutterTextInputViewScribbleInteractionBegan:(FlutterTextInputView*)textInputView {
1124 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1125 // the framework has finished transitioning to the Scribble channel.
1126 // https://github.com/flutter/flutter/pull/115296
1127 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1128}
1129
1130- (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView {
1131 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1132 // the framework has finished transitioning to the Scribble channel.
1133 // https://github.com/flutter/flutter/pull/115296
1134 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished"
1135 arguments:nil];
1136}
1137
1138- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1139 insertTextPlaceholderWithSize:(CGSize)size
1140 withClient:(int)client {
1141 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1142 // the framework has finished transitioning to the Scribble channel.
1143 // https://github.com/flutter/flutter/pull/115296
1144 [_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder"
1145 arguments:@[ @(client), @(size.width), @(size.height) ]];
1146}
1147
1148- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1149 removeTextPlaceholder:(int)client {
1150 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1151 // the framework has finished transitioning to the Scribble channel.
1152 // https://github.com/flutter/flutter/pull/115296
1153 [_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder"
1154 arguments:@[ @(client) ]];
1155}
1156
1157- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1158 didResignFirstResponderWithTextInputClient:(int)client {
1159 // When flutter text input view resign first responder, send a message to
1160 // framework to ensure the focus state is correct. This is useful when close
1161 // keyboard from platform side.
1162 [_textInputChannel.get() invokeMethod:@"TextInputClient.onConnectionClosed"
1163 arguments:@[ @(client) ]];
1164
1165 // Platform view's first responder detection logic:
1166 //
1167 // All text input widgets (e.g. EditableText) are backed by a dummy UITextInput view
1168 // in the TextInputPlugin. When this dummy UITextInput view resigns first responder,
1169 // check if any platform view becomes first responder. If any platform view becomes
1170 // first responder, send a "viewFocused" channel message to inform the framework to un-focus
1171 // the previously focused text input.
1172 //
1173 // Caveat:
1174 // 1. This detection logic does not cover the scenario when a platform view becomes
1175 // first responder without any flutter text input resigning its first responder status
1176 // (e.g. user tapping on platform view first). For now it works fine because the TextInputPlugin
1177 // does not track the focused platform view id (which is different from Android implementation).
1178 //
1179 // 2. This detection logic assumes that all text input widgets are backed by a dummy
1180 // UITextInput view in the TextInputPlugin, which may not hold true in the future.
1181
1182 // Have to check in the next run loop, because iOS requests the previous first responder to
1183 // resign before requesting the next view to become first responder.
1184 dispatch_async(dispatch_get_main_queue(), ^(void) {
1185 long platform_view_id = self.platformViewsController->FindFirstResponderPlatformViewId();
1186 if (platform_view_id == -1) {
1187 return;
1188 }
1189
1190 [_platformViewsChannel.get() invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1191 });
1192}
1193
1194#pragma mark - Undo Manager Delegate
1195
1196- (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1197 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
1198 [_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1199}
1200
1201- (UIView<UITextInput>*)activeTextInputView {
1202 return [[self textInputPlugin] textInputView];
1203}
1204
1205- (NSUndoManager*)undoManager {
1206 return self.viewController.undoManager;
1207}
1208
1209#pragma mark - Screenshot Delegate
1210
1211- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
1212 asBase64Encoded:(BOOL)base64Encode {
1213 FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell";
1214 return _shell->Screenshot(type, base64Encode);
1215}
1216
1217- (void)flutterViewAccessibilityDidCall {
1218 if (self.viewController.view.accessibilityElements == nil) {
1219 [self ensureSemanticsEnabled];
1220 }
1221}
1222
1223- (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
1224 return _binaryMessenger;
1225}
1226
1227- (NSObject<FlutterTextureRegistry>*)textureRegistry {
1228 return _textureRegistry;
1229}
1230
1231// For test only. Ideally we should create a dependency injector for all dependencies and
1232// remove this.
1233- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
1234 // Discard the previous messenger and keep the new one.
1235 if (binaryMessenger != _binaryMessenger) {
1237 [_binaryMessenger release];
1238 _binaryMessenger = [binaryMessenger retain];
1239 }
1240}
1241
1242#pragma mark - FlutterBinaryMessenger
1243
1244- (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1245 [self sendOnChannel:channel message:message binaryReply:nil];
1246}
1247
1248- (void)sendOnChannel:(NSString*)channel
1249 message:(NSData*)message
1250 binaryReply:(FlutterBinaryReply)callback {
1251 NSParameterAssert(channel);
1252 NSAssert(_shell && _shell->IsSetup(),
1253 @"Sending a message before the FlutterEngine has been run.");
1255 (callback == nil) ? nullptr
1256 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1257 ^(NSData* reply) {
1258 callback(reply);
1259 },
1260 _shell->GetTaskRunners().GetPlatformTaskRunner());
1261 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1262 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1263 : std::make_unique<flutter::PlatformMessage>(
1264 channel.UTF8String, flutter::CopyNSDataToMapping(message), response);
1265
1266 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1267 // platformMessage takes ownership of response.
1268 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1269}
1270
1271- (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
1273}
1274
1275- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
1276 binaryMessageHandler:
1278 return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1279}
1280
1282 setMessageHandlerOnChannel:(NSString*)channel
1283 binaryMessageHandler:(FlutterBinaryMessageHandler)handler
1284 taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
1285 NSParameterAssert(channel);
1286 if (_shell && _shell->IsSetup()) {
1287 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1288 handler, taskQueue);
1289 return _connections->AquireConnection(channel.UTF8String);
1290 } else {
1291 NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
1292 // Setting a handler to nil for a channel that has not yet been set up is a no-op.
1294 }
1295}
1296
1297- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1298 if (_shell && _shell->IsSetup()) {
1299 std::string channel = _connections->CleanupConnection(connection);
1300 if (!channel.empty()) {
1301 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1302 nil);
1303 }
1304 }
1305}
1306
1307#pragma mark - FlutterTextureRegistry
1308
1309- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
1310 int64_t textureId = _nextTextureId++;
1311 self.iosPlatformView->RegisterExternalTexture(textureId, texture);
1312 return textureId;
1313}
1314
1315- (void)unregisterTexture:(int64_t)textureId {
1316 _shell->GetPlatformView()->UnregisterTexture(textureId);
1317}
1318
1319- (void)textureFrameAvailable:(int64_t)textureId {
1320 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1321}
1322
1323- (NSString*)lookupKeyForAsset:(NSString*)asset {
1325}
1326
1327- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1328 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
1329}
1330
1331- (id<FlutterPluginRegistry>)pluginRegistry {
1332 return self;
1333}
1334
1335#pragma mark - FlutterPluginRegistry
1336
1337- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
1338 NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
1339 self.pluginPublications[pluginKey] = [NSNull null];
1340 FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
1341 flutterEngine:self];
1342 self.registrars[pluginKey] = result;
1343 return [result autorelease];
1344}
1345
1346- (BOOL)hasPlugin:(NSString*)pluginKey {
1347 return _pluginPublications[pluginKey] != nil;
1348}
1349
1350- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1351 return _pluginPublications[pluginKey];
1352}
1353
1354#pragma mark - Notifications
1355
1356#if APPLICATION_EXTENSION_API_ONLY
1357- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1358 [self flutterWillEnterForeground:notification];
1359}
1360
1361- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1362 [self flutterDidEnterBackground:notification];
1363}
1364#else
1365- (void)applicationWillEnterForeground:(NSNotification*)notification {
1366 [self flutterWillEnterForeground:notification];
1367}
1368
1369- (void)applicationDidEnterBackground:(NSNotification*)notification {
1370 [self flutterDidEnterBackground:notification];
1371}
1372#endif
1373
1374- (void)flutterWillEnterForeground:(NSNotification*)notification {
1375 [self setIsGpuDisabled:NO];
1376}
1377
1378- (void)flutterDidEnterBackground:(NSNotification*)notification {
1379 [self setIsGpuDisabled:YES];
1380 [self notifyLowMemory];
1381}
1382
1383- (void)onMemoryWarning:(NSNotification*)notification {
1384 [self notifyLowMemory];
1385}
1386
1387- (void)setIsGpuDisabled:(BOOL)value {
1388 if (_shell) {
1389 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1391 }
1392 _isGpuDisabled = value;
1393}
1394
1395#pragma mark - Locale updates
1396
1397- (void)onLocaleUpdated:(NSNotification*)notification {
1398 // Get and pass the user's preferred locale list to dart:ui.
1399 NSMutableArray<NSString*>* localeData = [[[NSMutableArray alloc] init] autorelease];
1400 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1401 for (NSString* localeID in preferredLocales) {
1402 NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
1403 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1404 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1405 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1406 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1407 if (!languageCode) {
1408 continue;
1409 }
1410 [localeData addObject:languageCode];
1411 [localeData addObject:(countryCode ? countryCode : @"")];
1412 [localeData addObject:(scriptCode ? scriptCode : @"")];
1413 [localeData addObject:(variantCode ? variantCode : @"")];
1414 }
1415 if (localeData.count == 0) {
1416 return;
1417 }
1418 [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1419}
1420
1421- (void)waitForFirstFrame:(NSTimeInterval)timeout
1422 callback:(void (^_Nonnull)(BOOL didTimeout))callback {
1423 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1424 dispatch_async(queue, ^{
1425 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1426 BOOL didTimeout =
1427 self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
1428 dispatch_async(dispatch_get_main_queue(), ^{
1429 callback(didTimeout);
1430 });
1431 });
1432}
1433
1434- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
1435 libraryURI:(/*nullable*/ NSString*)libraryURI
1436 initialRoute:(/*nullable*/ NSString*)initialRoute
1437 entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
1438 NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
1439 FlutterEngine* result = [[FlutterEngine alloc] initWithName:_labelPrefix
1440 project:_dartProject.get()
1441 allowHeadlessExecution:_allowHeadlessExecution];
1442 flutter::RunConfiguration configuration =
1443 [_dartProject.get() runConfigurationForEntrypoint:entrypoint
1444 libraryOrNil:libraryURI
1445 entrypointArgs:entrypointArgs];
1446
1449 // Static-cast safe since this class always creates PlatformViewIOS instances.
1450 flutter::PlatformViewIOS* ios_platform_view =
1451 static_cast<flutter::PlatformViewIOS*>(platform_view.get());
1452 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->GetIosContext();
1453 FML_DCHECK(context);
1454
1455 // Lambda captures by pointers to ObjC objects are fine here because the
1456 // create call is synchronous.
1458 [result, context](flutter::Shell& shell) {
1460 return std::make_unique<flutter::PlatformViewIOS>(
1461 shell, context, result->_platformViewsController, shell.GetTaskRunners());
1462 };
1463
1465 [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
1466
1467 std::string cppInitialRoute;
1468 if (initialRoute) {
1469 cppInitialRoute = [initialRoute UTF8String];
1470 }
1471
1472 std::unique_ptr<flutter::Shell> shell = _shell->Spawn(
1473 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1474
1475 result->_threadHost = _threadHost;
1476 result->_profiler = _profiler;
1477 result->_profiler_metrics = _profiler_metrics;
1478 result->_isGpuDisabled = _isGpuDisabled;
1479 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1480 return [result autorelease];
1481}
1482
1483- (const flutter::ThreadHost&)threadHost {
1484 return *_threadHost;
1485}
1486
1487- (FlutterDartProject*)project {
1488 return _dartProject.get();
1489}
1490
1491- (BOOL)isUsingImpeller {
1492 return self.project.isImpellerEnabled;
1493}
1494
1495@end
1496
1497@implementation FlutterEngineRegistrar {
1498 NSString* _pluginKey;
1499}
1500
1501- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
1502 self = [super init];
1503 NSAssert(self, @"Super init cannot be nil");
1504 _pluginKey = [pluginKey copy];
1505 _flutterEngine = flutterEngine;
1506 return self;
1507}
1508
1509- (void)dealloc {
1510 [_pluginKey release];
1511 [super dealloc];
1512}
1513
1514- (NSObject<FlutterBinaryMessenger>*)messenger {
1516}
1517
1518- (NSObject<FlutterTextureRegistry>*)textures {
1520}
1521
1522- (void)publish:(NSObject*)value {
1524}
1525
1526- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
1527 channel:(FlutterMethodChannel*)channel {
1528 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1529 [delegate handleMethodCall:call result:result];
1530 }];
1531}
1532
1533- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate
1534 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1535 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1536 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
1537 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1538 (id<FlutterAppLifeCycleProvider>)appDelegate;
1539 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1540 }
1541}
1542
1543- (NSString*)lookupKeyForAsset:(NSString*)asset {
1544 return [_flutterEngine lookupKeyForAsset:asset];
1545}
1546
1547- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1548 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1549}
1550
1551- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1552 withId:(NSString*)factoryId {
1553 [self registerViewFactory:factory
1554 withId:factoryId
1555 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1556}
1557
1558- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1559 withId:(NSString*)factoryId
1560 gestureRecognizersBlockingPolicy:
1561 (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
1562 [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
1563 gestureRecognizersBlockingPolicy);
1564}
1565
1566@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)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
FlutterPlatformViewGestureRecognizersBlockingPolicy
static SkScalar center(float pos0, float pos1)
Maintains a current integer assigned to a name (connections).
static Connection MakeErrorConnection(int errCode)
static NSObject< FlutterTaskQueue > * MakeBackgroundTaskQueue()
void SetOwnerViewController(const fml::WeakNSObject< FlutterViewController > &owner_controller)
const std::shared_ptr< IOSContext > & GetIosContext()
Specifies all the configuration required by the runtime library to launch the root isolate....
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, const TaskRunners &task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer, bool is_gpu_disabled=false)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition shell.cc:167
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition shell.h:119
static void EnsureInitializedForCurrentThread()
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
T get() const __attribute((ns_returns_not_retained))
@ kNormal
Default priority level.
@ kRaster
Suitable for thread which raster data.
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
@ kDisplay
Suitable for threads which generate data for the display.
static void SetCurrentThreadName(const ThreadConfig &config)
Definition thread.cc:135
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition time_delta.h:46
void reset(NST *object=Traits::InvalidValue(), scoped_policy::OwnershipPolicy policy=scoped_policy::OwnershipPolicy::kAssume)
std::vector< std::shared_ptr< FakeTexture > > textures
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3329
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1153
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1075
VkQueue queue
Definition main.cc:55
AtkStateType state
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
uint8_t value
GAsyncResult * result
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
#define FML_LOG(severity)
Definition logging.h:82
#define FML_DCHECK(condition)
Definition logging.h:103
NSObject< FlutterBinaryMessenger > * parent
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
NSString * lookupKeyForAsset:(NSString *asset)
FlutterEngine * flutterEngine
NSObject< FlutterBinaryMessenger > * binaryMessenger
NSObject< FlutterTextureRegistry > * textureRegistry
void recreatePlatformViewController()
NSString * generateThreadLabel:(NSString *labelPrefix)
NSMutableDictionary * pluginPublications
id< NSObject > flutterViewControllerWillDeallocObserver
flutter::ThreadHost makeThreadHost:(NSString *threadLabel)
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
instancetype sharedInstance()
void setUpIndirectScribbleInteraction:(id< FlutterViewResponder > viewResponder)
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
id< FlutterIndirectScribbleDelegate > indirectScribbleDelegate
NSObject< FlutterTextureRegistry > * parent
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
fml::WeakNSObject< FlutterViewController > getWeakNSObject()
BOOL forceSoftwareRendering
Definition FlutterView.h:49
fml::scoped_nsobject< FlutterMethodChannel > _undoManagerChannel
fml::scoped_nsobject< FlutterSpellCheckPlugin > _spellCheckPlugin
fml::scoped_nsobject< FlutterBasicMessageChannel > _lifecycleChannel
NSString *const FlutterDefaultDartEntrypoint
fml::WeakNSObject< FlutterViewController > _viewController
NSString *const kFlutterEngineWillDealloc
fml::scoped_nsobject< FlutterMethodChannel > _localizationChannel
fml::scoped_nsobject< FlutterMethodChannel > _navigationChannel
std::shared_ptr< flutter::ProfilerMetricsIOS > _profiler_metrics
std::shared_ptr< flutter::SamplingProfiler > _profiler
fml::scoped_nsobject< FlutterMethodChannel > _spellCheckChannel
fml::scoped_nsobject< FlutterPlatformPlugin > _platformPlugin
std::unique_ptr< flutter::Shell > _shell
fml::scoped_nsobject< FlutterMethodChannel > _screenshotChannel
fml::scoped_nsobject< FlutterMethodChannel > _platformViewsChannel
fml::scoped_nsobject< FlutterMethodChannel > _scribbleChannel
BOOL _restorationEnabled
fml::scoped_nsobject< FlutterBasicMessageChannel > _keyEventChannel
fml::scoped_nsobject< FlutterDartVMServicePublisher > _publisher
BOOL _allowHeadlessExecution
fml::scoped_nsobject< FlutterBasicMessageChannel > _systemChannel
std::shared_ptr< flutter::FlutterPlatformViewsController > _platformViewsController
fml::scoped_nsobject< FlutterUndoManagerPlugin > _undoManagerPlugin
NSString * _labelPrefix
static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig &config)
NSString *const kFlutterKeyDataChannel
std::unique_ptr< fml::WeakNSObjectFactory< FlutterEngine > > _weakFactory
fml::scoped_nsobject< FlutterBasicMessageChannel > _settingsChannel
fml::scoped_nsobject< FlutterMethodChannel > _platformChannel
std::unique_ptr< flutter::ConnectionCollection > _connections
NSString *const FlutterDefaultInitialRoute
fml::scoped_nsobject< FlutterRestorationPlugin > _restorationPlugin
flutter::IOSRenderingAPI _renderingApi
FlutterTextureRegistryRelay * _textureRegistry
fml::scoped_nsobject< FlutterTextInputPlugin > _textInputPlugin
static constexpr int kNumProfilerSamplesPerSec
std::shared_ptr< flutter::ThreadHost > _threadHost
fml::scoped_nsobject< FlutterMethodChannel > _restorationChannel
fml::scoped_nsobject< FlutterMethodChannel > _textInputChannel
int64_t _nextTextureId
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
size_t length
Win32Message message
__weak FlutterEngine * _flutterEngine
return FALSE
FlTexture * texture
SK_API sk_sp< SkSurface > ios(9.0)
Definition copy.py:1
constexpr int64_t kFlutterImplicitViewId
Definition constants.h:35
fml::MallocMapping CopyNSDataToMapping(NSData *data)
bool EnableTracingIfNecessary(const Settings &vm_settings)
Enables tracing in the process so that JIT mode VMs may be launched. Explicitly enabling tracing is n...
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
GpuAvailability
Values for |Shell::SetGpuAvailability|.
Definition shell.h:62
@ kAvailable
Indicates that GPU operations should be permitted.
RefPtr< T > MakeRefCounted(Args &&... args)
Definition ref_ptr.h:248
const myers::Point & get(const myers::Segment &)
Definition run.py:1
Definition ref_ptr.h:256
const Scalar scale
Function-pointer-based versions of the APIs above.
Definition embedder.h:3317
uint64_t synthesized
Definition key_data.h:70
uint64_t logical
Definition key_data.h:66
uint64_t physical
Definition key_data.h:65
KeyEventType type
Definition key_data.h:64
uint64_t timestamp
Definition key_data.h:63
static std::string MakeThreadName(Type type, const std::string &prefix)
Use the prefix and thread type to generator a thread name.
The collection of all the threads used by the engine.
Definition thread_host.h:21
The ThreadConfig is the thread info include thread name, thread priority.
Definition thread.h:35
ThreadPriority priority
Definition thread.h:45
const uintptr_t id
#define ERROR(message)
int BOOL