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/shell/common/engine.h" 15#include "flutter/shell/common/platform_view.h" 16#include "flutter/shell/common/shell.h" 17#include "flutter/shell/common/switches.h" 18#include "flutter/shell/common/thread_host.h" 19#include "flutter/shell/platform/darwin/common/command_line.h" 20#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h" 21#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" 22#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h" 23#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" 24#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" 25#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" 26#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" 27#import "flutter/shell/platform/darwin/ios/ios_surface.h" 28#import "flutter/shell/platform/darwin/ios/platform_view_ios.h" 29 30@interface FlutterEngine () <FlutterTextInputDelegate, FlutterBinaryMessenger> 31// Maintains a dictionary of plugin names that have registered with the engine. Used by 32// FlutterEngineRegistrar to implement a FlutterPluginRegistrar. 33@property(nonatomic, readonly) NSMutableDictionary* pluginPublications; 34 35@property(nonatomic, readwrite, copy) NSString* isolateId; 36@end 37 38@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar> 39- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine; 40@end 41 42@implementation FlutterEngine { 43 fml::scoped_nsobject<FlutterDartProject> _dartProject; 44 flutter::ThreadHost _threadHost; 45 std::unique_ptr<flutter::Shell> _shell; 46 NSString* _labelPrefix; 47 std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory; 48 49 fml::WeakPtr<FlutterViewController> _viewController; 50 fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher; 51 52 std::unique_ptr<flutter::FlutterPlatformViewsController> _platformViewsController; 53 54 // Channels 55 fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin; 56 fml::scoped_nsobject<FlutterTextInputPlugin> _textInputPlugin; 57 fml::scoped_nsobject<FlutterMethodChannel> _localizationChannel; 58 fml::scoped_nsobject<FlutterMethodChannel> _navigationChannel; 59 fml::scoped_nsobject<FlutterMethodChannel> _platformChannel; 60 fml::scoped_nsobject<FlutterMethodChannel> _platformViewsChannel; 61 fml::scoped_nsobject<FlutterMethodChannel> _textInputChannel; 62 fml::scoped_nsobject<FlutterBasicMessageChannel> _lifecycleChannel; 63 fml::scoped_nsobject<FlutterBasicMessageChannel> _systemChannel; 64 fml::scoped_nsobject<FlutterBasicMessageChannel> _settingsChannel; 65 66 int64_t _nextTextureId; 67 68 BOOL _allowHeadlessExecution; 69 FlutterBinaryMessengerRelay* _binaryMessenger; 70} 71 72- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { 73 return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; 74} 75 76- (instancetype)initWithName:(NSString*)labelPrefix 77 project:(FlutterDartProject*)projectOrNil 78 allowHeadlessExecution:(BOOL)allowHeadlessExecution { 79 self = [super init]; 80 NSAssert(self, @"Super init cannot be nil"); 81 NSAssert(labelPrefix, @"labelPrefix is required"); 82 83 _allowHeadlessExecution = allowHeadlessExecution; 84 _labelPrefix = [labelPrefix copy]; 85 86 _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self); 87 88 if (projectOrNil == nil) 89 _dartProject.reset([[FlutterDartProject alloc] init]); 90 else 91 _dartProject.reset([projectOrNil retain]); 92 93 _pluginPublications = [NSMutableDictionary new]; 94 _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); 95 96 [self setupChannels]; 97 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; 98 99 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 100 [center addObserver:self 101 selector:@selector(onMemoryWarning:) 102 name:UIApplicationDidReceiveMemoryWarningNotification 103 object:nil]; 104 105 return self; 106} 107 108- (void)dealloc { 109 [_pluginPublications release]; 110 _binaryMessenger.parent = nil; 111 [_binaryMessenger release]; 112 113 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 114 [center removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 115 116 [super dealloc]; 117} 118 119- (flutter::Shell&)shell { 120 FML_DCHECK(_shell); 121 return *_shell; 122} 123 124- (fml::WeakPtr<FlutterEngine>)getWeakPtr { 125 return _weakFactory->GetWeakPtr(); 126} 127 128- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics { 129 if (!self.platformView) { 130 return; 131 } 132 self.platformView->SetViewportMetrics(std::move(viewportMetrics)); 133} 134 135- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet { 136 if (!self.platformView) { 137 return; 138 } 139 self.platformView->DispatchPointerDataPacket(std::move(packet)); 140} 141 142- (fml::WeakPtr<flutter::PlatformView>)platformView { 143 FML_DCHECK(_shell); 144 return _shell->GetPlatformView(); 145} 146 147- (flutter::PlatformViewIOS*)iosPlatformView { 148 FML_DCHECK(_shell); 149 return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get()); 150} 151 152- (fml::RefPtr<fml::TaskRunner>)platformTaskRunner { 153 FML_DCHECK(_shell); 154 return _shell->GetTaskRunners().GetPlatformTaskRunner(); 155} 156 157- (fml::RefPtr<fml::TaskRunner>)GPUTaskRunner { 158 FML_DCHECK(_shell); 159 return _shell->GetTaskRunners().GetGPUTaskRunner(); 160} 161 162- (void)ensureSemanticsEnabled { 163 self.iosPlatformView->SetSemanticsEnabled(true); 164} 165 166- (void)setViewController:(FlutterViewController*)viewController { 167 FML_DCHECK(self.iosPlatformView); 168 _viewController = [viewController getWeakPtr]; 169 self.iosPlatformView->SetOwnerViewController(_viewController); 170 [self maybeSetupPlatformViewChannels]; 171} 172 173- (void)notifyViewControllerDeallocated { 174 if (!_allowHeadlessExecution) { 175 [self destroyContext]; 176 } 177} 178 179- (void)destroyContext { 180 [self resetChannels]; 181 self.isolateId = nil; 182 _shell.reset(); 183 _threadHost.Reset(); 184 _platformViewsController.reset(); 185} 186 187- (FlutterViewController*)viewController { 188 if (!_viewController) { 189 return nil; 190 } 191 return _viewController.get(); 192} 193 194- (FlutterPlatformPlugin*)platformPlugin { 195 return _platformPlugin.get(); 196} 197- (flutter::FlutterPlatformViewsController*)platformViewsController { 198 return _platformViewsController.get(); 199} 200- (FlutterTextInputPlugin*)textInputPlugin { 201 return _textInputPlugin.get(); 202} 203- (FlutterMethodChannel*)localizationChannel { 204 return _localizationChannel.get(); 205} 206- (FlutterMethodChannel*)navigationChannel { 207 return _navigationChannel.get(); 208} 209- (FlutterMethodChannel*)platformChannel { 210 return _platformChannel.get(); 211} 212- (FlutterMethodChannel*)textInputChannel { 213 return _textInputChannel.get(); 214} 215- (FlutterBasicMessageChannel*)lifecycleChannel { 216 return _lifecycleChannel.get(); 217} 218- (FlutterBasicMessageChannel*)systemChannel { 219 return _systemChannel.get(); 220} 221- (FlutterBasicMessageChannel*)settingsChannel { 222 return _settingsChannel.get(); 223} 224 225- (NSURL*)observatoryUrl { 226 return [_publisher.get() url]; 227} 228 229- (void)resetChannels { 230 _localizationChannel.reset(); 231 _navigationChannel.reset(); 232 _platformChannel.reset(); 233 _platformViewsChannel.reset(); 234 _textInputChannel.reset(); 235 _lifecycleChannel.reset(); 236 _systemChannel.reset(); 237 _settingsChannel.reset(); 238} 239 240// If you add a channel, be sure to also update `resetChannels`. 241// Channels get a reference to the engine, and therefore need manual 242// cleanup for proper collection. 243- (void)setupChannels { 244 // This will be invoked once the shell is done setting up and the isolate ID 245 // for the UI isolate is available. 246 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate" 247 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) { 248 self.isolateId = [[FlutterStringCodec sharedInstance] decode:message]; 249 }]; 250 251 _localizationChannel.reset([[FlutterMethodChannel alloc] 252 initWithName:@"flutter/localization" 253 binaryMessenger:self.binaryMessenger 254 codec:[FlutterJSONMethodCodec sharedInstance]]); 255 256 _navigationChannel.reset([[FlutterMethodChannel alloc] 257 initWithName:@"flutter/navigation" 258 binaryMessenger:self.binaryMessenger 259 codec:[FlutterJSONMethodCodec sharedInstance]]); 260 261 _platformChannel.reset([[FlutterMethodChannel alloc] 262 initWithName:@"flutter/platform" 263 binaryMessenger:self.binaryMessenger 264 codec:[FlutterJSONMethodCodec sharedInstance]]); 265 266 _platformViewsChannel.reset([[FlutterMethodChannel alloc] 267 initWithName:@"flutter/platform_views" 268 binaryMessenger:self.binaryMessenger 269 codec:[FlutterStandardMethodCodec sharedInstance]]); 270 271 _textInputChannel.reset([[FlutterMethodChannel alloc] 272 initWithName:@"flutter/textinput" 273 binaryMessenger:self.binaryMessenger 274 codec:[FlutterJSONMethodCodec sharedInstance]]); 275 276 _lifecycleChannel.reset([[FlutterBasicMessageChannel alloc] 277 initWithName:@"flutter/lifecycle" 278 binaryMessenger:self.binaryMessenger 279 codec:[FlutterStringCodec sharedInstance]]); 280 281 _systemChannel.reset([[FlutterBasicMessageChannel alloc] 282 initWithName:@"flutter/system" 283 binaryMessenger:self.binaryMessenger 284 codec:[FlutterJSONMessageCodec sharedInstance]]); 285 286 _settingsChannel.reset([[FlutterBasicMessageChannel alloc] 287 initWithName:@"flutter/settings" 288 binaryMessenger:self.binaryMessenger 289 codec:[FlutterJSONMessageCodec sharedInstance]]); 290 291 _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]); 292 _textInputPlugin.get().textInputDelegate = self; 293 294 _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]); 295} 296 297- (void)maybeSetupPlatformViewChannels { 298 if (_shell && self.shell.IsSetup()) { 299 [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 300 [_platformPlugin.get() handleMethodCall:call result:result]; 301 }]; 302 303 [_platformViewsChannel.get() 304 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 305 _platformViewsController->OnMethodCall(call, result); 306 }]; 307 308 [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 309 [_textInputPlugin.get() handleMethodCall:call result:result]; 310 }]; 311 self.iosPlatformView->SetTextInputPlugin(_textInputPlugin); 312 } 313} 314 315- (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type 316 base64Encode:(bool)base64Encode { 317 return self.shell.Screenshot(type, base64Encode); 318} 319 320- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil { 321 // Launch the Dart application with the inferred run configuration. 322 self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint 323 libraryOrNil:libraryOrNil]); 324} 325 326- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { 327 if (_shell != nullptr) { 328 FML_LOG(WARNING) << "This FlutterEngine was already invoked."; 329 return NO; 330 } 331 332 static size_t shellCount = 1; 333 334 auto settings = [_dartProject.get() settings]; 335 336 if (libraryURI) { 337 FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library"; 338 settings.advisory_script_entrypoint = entrypoint.UTF8String; 339 settings.advisory_script_uri = libraryURI.UTF8String; 340 } else if (entrypoint) { 341 settings.advisory_script_entrypoint = entrypoint.UTF8String; 342 settings.advisory_script_uri = std::string("main.dart"); 343 } else { 344 settings.advisory_script_entrypoint = std::string("main"); 345 settings.advisory_script_uri = std::string("main.dart"); 346 } 347 348 const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++]; 349 FML_DLOG(INFO) << "Creating threadHost for " << threadLabel.UTF8String; 350 // The current thread will be used as the platform thread. Ensure that the message loop is 351 // initialized. 352 fml::MessageLoop::EnsureInitializedForCurrentThread(); 353 354 _threadHost = {threadLabel.UTF8String, // label 355 flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::GPU | 356 flutter::ThreadHost::Type::IO}; 357 358 // Lambda captures by pointers to ObjC objects are fine here because the 359 // create call is 360 // synchronous. 361 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view = 362 [](flutter::Shell& shell) { 363 return std::make_unique<flutter::PlatformViewIOS>(shell, shell.GetTaskRunners()); 364 }; 365 366 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer = 367 [](flutter::Shell& shell) { 368 return std::make_unique<flutter::Rasterizer>(shell, shell.GetTaskRunners()); 369 }; 370 371 if (flutter::IsIosEmbeddedViewsPreviewEnabled()) { 372 // Embedded views requires the gpu and the platform views to be the same. 373 // The plan is to eventually dynamically merge the threads when there's a 374 // platform view in the layer tree. 375 // For now we use a fixed thread configuration with the same thread used as the 376 // gpu and platform task runner. 377 // TODO(amirh/chinmaygarde): remove this, and dynamically change the thread configuration. 378 // https://github.com/flutter/flutter/issues/23975 379 380 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label 381 fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform 382 fml::MessageLoop::GetCurrent().GetTaskRunner(), // gpu 383 _threadHost.ui_thread->GetTaskRunner(), // ui 384 _threadHost.io_thread->GetTaskRunner() // io 385 ); 386 // Create the shell. This is a blocking operation. 387 _shell = flutter::Shell::Create(std::move(task_runners), // task runners 388 std::move(settings), // settings 389 on_create_platform_view, // platform view creation 390 on_create_rasterizer // rasterzier creation 391 ); 392 } else { 393 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label 394 fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform 395 _threadHost.gpu_thread->GetTaskRunner(), // gpu 396 _threadHost.ui_thread->GetTaskRunner(), // ui 397 _threadHost.io_thread->GetTaskRunner() // io 398 ); 399 // Create the shell. This is a blocking operation. 400 _shell = flutter::Shell::Create(std::move(task_runners), // task runners 401 std::move(settings), // settings 402 on_create_platform_view, // platform view creation 403 on_create_rasterizer // rasterzier creation 404 ); 405 } 406 407 if (_shell == nullptr) { 408 FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: " 409 << entrypoint.UTF8String; 410 } else { 411 [self setupChannels]; 412 if (!_platformViewsController) { 413 _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); 414 } 415 _publisher.reset([[FlutterObservatoryPublisher alloc] init]); 416 [self maybeSetupPlatformViewChannels]; 417 } 418 419 return _shell != nullptr; 420} 421 422- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { 423 if ([self createShell:entrypoint libraryURI:libraryURI]) { 424 [self launchEngine:entrypoint libraryURI:libraryURI]; 425 } 426 427 return _shell != nullptr; 428} 429 430- (BOOL)runWithEntrypoint:(NSString*)entrypoint { 431 return [self runWithEntrypoint:entrypoint libraryURI:nil]; 432} 433 434#pragma mark - Text input delegate 435 436- (void)updateEditingClient:(int)client withState:(NSDictionary*)state { 437 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" 438 arguments:@[ @(client), state ]]; 439} 440 441- (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state 442 withClient:(int)client 443 withPosition:(NSDictionary*)position { 444 NSString* stateString; 445 switch (state) { 446 case FlutterFloatingCursorDragStateStart: 447 stateString = @"FloatingCursorDragState.start"; 448 break; 449 case FlutterFloatingCursorDragStateUpdate: 450 stateString = @"FloatingCursorDragState.update"; 451 break; 452 case FlutterFloatingCursorDragStateEnd: 453 stateString = @"FloatingCursorDragState.end"; 454 break; 455 } 456 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor" 457 arguments:@[ @(client), stateString, position ]]; 458} 459 460- (void)performAction:(FlutterTextInputAction)action withClient:(int)client { 461 NSString* actionString; 462 switch (action) { 463 case FlutterTextInputActionUnspecified: 464 // Where did the term "unspecified" come from? iOS has a "default" and Android 465 // has "unspecified." These 2 terms seem to mean the same thing but we need 466 // to pick just one. "unspecified" was chosen because "default" is often a 467 // reserved word in languages with switch statements (dart, java, etc). 468 actionString = @"TextInputAction.unspecified"; 469 break; 470 case FlutterTextInputActionDone: 471 actionString = @"TextInputAction.done"; 472 break; 473 case FlutterTextInputActionGo: 474 actionString = @"TextInputAction.go"; 475 break; 476 case FlutterTextInputActionSend: 477 actionString = @"TextInputAction.send"; 478 break; 479 case FlutterTextInputActionSearch: 480 actionString = @"TextInputAction.search"; 481 break; 482 case FlutterTextInputActionNext: 483 actionString = @"TextInputAction.next"; 484 break; 485 case FlutterTextInputActionContinue: 486 actionString = @"TextInputAction.continue"; 487 break; 488 case FlutterTextInputActionJoin: 489 actionString = @"TextInputAction.join"; 490 break; 491 case FlutterTextInputActionRoute: 492 actionString = @"TextInputAction.route"; 493 break; 494 case FlutterTextInputActionEmergencyCall: 495 actionString = @"TextInputAction.emergencyCall"; 496 break; 497 case FlutterTextInputActionNewline: 498 actionString = @"TextInputAction.newline"; 499 break; 500 } 501 [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction" 502 arguments:@[ @(client), actionString ]]; 503} 504 505#pragma mark - Screenshot Delegate 506 507- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type 508 asBase64Encoded:(BOOL)base64Encode { 509 FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; 510 return _shell->Screenshot(type, base64Encode); 511} 512 513- (NSObject<FlutterBinaryMessenger>*)binaryMessenger { 514 return _binaryMessenger; 515} 516 517#pragma mark - FlutterBinaryMessenger 518 519- (void)sendOnChannel:(NSString*)channel message:(NSData*)message { 520 [self sendOnChannel:channel message:message binaryReply:nil]; 521} 522 523- (void)sendOnChannel:(NSString*)channel 524 message:(NSData*)message 525 binaryReply:(FlutterBinaryReply)callback { 526 NSAssert(channel, @"The channel must not be null"); 527 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response = 528 (callback == nil) ? nullptr 529 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>( 530 ^(NSData* reply) { 531 callback(reply); 532 }, 533 _shell->GetTaskRunners().GetPlatformTaskRunner()); 534 fml::RefPtr<flutter::PlatformMessage> platformMessage = 535 (message == nil) ? fml::MakeRefCounted<flutter::PlatformMessage>(channel.UTF8String, response) 536 : fml::MakeRefCounted<flutter::PlatformMessage>( 537 channel.UTF8String, flutter::GetVectorFromNSData(message), response); 538 539 _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); 540} 541 542- (void)setMessageHandlerOnChannel:(NSString*)channel 543 binaryMessageHandler:(FlutterBinaryMessageHandler)handler { 544 NSAssert(channel, @"The channel must not be null"); 545 FML_DCHECK(_shell && _shell->IsSetup()); 546 self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler); 547} 548 549#pragma mark - FlutterTextureRegistry 550 551- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture { 552 int64_t textureId = _nextTextureId++; 553 self.iosPlatformView->RegisterExternalTexture(textureId, texture); 554 return textureId; 555} 556 557- (void)unregisterTexture:(int64_t)textureId { 558 _shell->GetPlatformView()->UnregisterTexture(textureId); 559} 560 561- (void)textureFrameAvailable:(int64_t)textureId { 562 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); 563} 564 565- (NSString*)lookupKeyForAsset:(NSString*)asset { 566 return [FlutterDartProject lookupKeyForAsset:asset]; 567} 568 569- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { 570 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; 571} 572 573- (id<FlutterPluginRegistry>)pluginRegistry { 574 return self; 575} 576 577#pragma mark - FlutterPluginRegistry 578 579- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey { 580 NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); 581 self.pluginPublications[pluginKey] = [NSNull null]; 582 return [[[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey flutterEngine:self] autorelease]; 583} 584 585- (BOOL)hasPlugin:(NSString*)pluginKey { 586 return _pluginPublications[pluginKey] != nil; 587} 588 589- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { 590 return _pluginPublications[pluginKey]; 591} 592 593#pragma mark - Memory Notifications 594 595- (void)onMemoryWarning:(NSNotification*)notification { 596 if (_shell) { 597 _shell->NotifyLowMemoryWarning(); 598 } 599 [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}]; 600} 601 602@end 603 604@implementation FlutterEngineRegistrar { 605 NSString* _pluginKey; 606 FlutterEngine* _flutterEngine; 607} 608 609- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { 610 self = [super init]; 611 NSAssert(self, @"Super init cannot be nil"); 612 _pluginKey = [pluginKey retain]; 613 _flutterEngine = [flutterEngine retain]; 614 return self; 615} 616 617- (void)dealloc { 618 [_pluginKey release]; 619 [_flutterEngine release]; 620 [super dealloc]; 621} 622 623- (NSObject<FlutterBinaryMessenger>*)messenger { 624 return _flutterEngine.binaryMessenger; 625} 626 627- (NSObject<FlutterTextureRegistry>*)textures { 628 return _flutterEngine; 629} 630 631- (void)publish:(NSObject*)value { 632 _flutterEngine.pluginPublications[_pluginKey] = value; 633} 634 635- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate 636 channel:(FlutterMethodChannel*)channel { 637 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 638 [delegate handleMethodCall:call result:result]; 639 }]; 640} 641 642- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate { 643 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate]; 644 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) { 645 id<FlutterAppLifeCycleProvider> lifeCycleProvider = 646 (id<FlutterAppLifeCycleProvider>)appDelegate; 647 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate]; 648 } 649} 650 651- (NSString*)lookupKeyForAsset:(NSString*)asset { 652 return [_flutterEngine lookupKeyForAsset:asset]; 653} 654 655- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { 656 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package]; 657} 658 659- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory 660 withId:(NSString*)factoryId { 661 [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId); 662} 663 664@end 665