Flutter iOS Embedder
FlutterEngineTest.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 #import <Foundation/Foundation.h>
6 #import <OCMock/OCMock.h>
7 #import <XCTest/XCTest.h>
8 
9 #import <objc/runtime.h>
10 
11 #import "flutter/common/settings.h"
12 #include "flutter/fml/synchronization/sync_switch.h"
20 
22 
24 @property(nonatomic) BOOL ensureSemanticsEnabledCalled;
25 @end
26 
27 @implementation FlutterEngineSpy
28 
29 - (void)ensureSemanticsEnabled {
30  _ensureSemanticsEnabledCalled = YES;
31 }
32 
33 @end
34 
36 
37 @end
38 
39 /// FlutterBinaryMessengerRelay used for testing that setting FlutterEngine.binaryMessenger to
40 /// the current instance doesn't trigger a use-after-free bug.
41 ///
42 /// See: testSetBinaryMessengerToSameBinaryMessenger
44 @property(nonatomic, assign) BOOL failOnDealloc;
45 @end
46 
47 @implementation FakeBinaryMessengerRelay
48 - (void)dealloc {
49  if (_failOnDealloc) {
50  XCTFail("FakeBinaryMessageRelay should not be deallocated");
51  }
52 }
53 @end
54 
55 @interface FlutterEngineTest : XCTestCase
56 @end
57 
58 @implementation FlutterEngineTest
59 
60 - (void)setUp {
61 }
62 
63 - (void)tearDown {
64 }
65 
66 - (void)testCreate {
67  FlutterDartProject* project = [[FlutterDartProject alloc] init];
68  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
69  XCTAssertNotNil(engine);
70 }
71 
72 - (void)testShellGetters {
73  FlutterDartProject* project = [[FlutterDartProject alloc] init];
74  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
75  XCTAssertNotNil(engine);
76 
77  // Ensure getters don't deref _shell when it's null, and instead return nullptr.
78  XCTAssertEqual(engine.platformTaskRunner.get(), nullptr);
79  XCTAssertEqual(engine.uiTaskRunner.get(), nullptr);
80  XCTAssertEqual(engine.rasterTaskRunner.get(), nullptr);
81 }
82 
83 - (void)testInfoPlist {
84  // Check the embedded Flutter.framework Info.plist, not the linked dylib.
85  NSURL* flutterFrameworkURL =
86  [NSBundle.mainBundle.privateFrameworksURL URLByAppendingPathComponent:@"Flutter.framework"];
87  NSBundle* flutterBundle = [NSBundle bundleWithURL:flutterFrameworkURL];
88  XCTAssertEqualObjects(flutterBundle.bundleIdentifier, @"io.flutter.flutter");
89 
90  NSDictionary<NSString*, id>* infoDictionary = flutterBundle.infoDictionary;
91 
92  // OS version can have one, two, or three digits: "8", "8.0", "8.0.0"
93  NSError* regexError = NULL;
94  NSRegularExpression* osVersionRegex =
95  [NSRegularExpression regularExpressionWithPattern:@"((0|[1-9]\\d*)\\.)*(0|[1-9]\\d*)"
96  options:NSRegularExpressionCaseInsensitive
97  error:&regexError];
98  XCTAssertNil(regexError);
99 
100  // Smoke test the test regex.
101  NSString* testString = @"9";
102  NSUInteger versionMatches =
103  [osVersionRegex numberOfMatchesInString:testString
104  options:NSMatchingAnchored
105  range:NSMakeRange(0, testString.length)];
106  XCTAssertEqual(versionMatches, 1UL);
107  testString = @"9.1";
108  versionMatches = [osVersionRegex numberOfMatchesInString:testString
109  options:NSMatchingAnchored
110  range:NSMakeRange(0, testString.length)];
111  XCTAssertEqual(versionMatches, 1UL);
112  testString = @"9.0.1";
113  versionMatches = [osVersionRegex numberOfMatchesInString:testString
114  options:NSMatchingAnchored
115  range:NSMakeRange(0, testString.length)];
116  XCTAssertEqual(versionMatches, 1UL);
117  testString = @".0.1";
118  versionMatches = [osVersionRegex numberOfMatchesInString:testString
119  options:NSMatchingAnchored
120  range:NSMakeRange(0, testString.length)];
121  XCTAssertEqual(versionMatches, 0UL);
122 
123  // Test Info.plist values.
124  NSString* minimumOSVersion = infoDictionary[@"MinimumOSVersion"];
125  versionMatches = [osVersionRegex numberOfMatchesInString:minimumOSVersion
126  options:NSMatchingAnchored
127  range:NSMakeRange(0, minimumOSVersion.length)];
128  XCTAssertEqual(versionMatches, 1UL);
129 
130  // SHA length is 40.
131  XCTAssertEqual(((NSString*)infoDictionary[@"FlutterEngine"]).length, 40UL);
132 
133  // {clang_version} placeholder is 15 characters. The clang string version
134  // is longer than that, so check if the placeholder has been replaced, without
135  // actually checking a literal string, which could be different on various machines.
136  XCTAssertTrue(((NSString*)infoDictionary[@"ClangVersion"]).length > 15UL);
137 }
138 
139 - (void)testDeallocated {
140  __weak FlutterEngine* weakEngine = nil;
141  @autoreleasepool {
142  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
143  weakEngine = engine;
144  [engine run];
145  XCTAssertNotNil(weakEngine);
146  }
147  XCTAssertNil(weakEngine);
148 }
149 
150 - (void)testSendMessageBeforeRun {
151  FlutterDartProject* project = [[FlutterDartProject alloc] init];
152  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
153  XCTAssertNotNil(engine);
154  XCTAssertThrows([engine.binaryMessenger
155  sendOnChannel:@"foo"
156  message:[@"bar" dataUsingEncoding:NSUTF8StringEncoding]
157  binaryReply:nil]);
158 }
159 
160 - (void)testSetMessageHandlerBeforeRun {
161  FlutterDartProject* project = [[FlutterDartProject alloc] init];
162  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
163  XCTAssertNotNil(engine);
164  XCTAssertThrows([engine.binaryMessenger
165  setMessageHandlerOnChannel:@"foo"
166  binaryMessageHandler:^(NSData* _Nullable message, FlutterBinaryReply _Nonnull reply){
167 
168  }]);
169 }
170 
171 - (void)testNilSetMessageHandlerBeforeRun {
172  FlutterDartProject* project = [[FlutterDartProject alloc] init];
173  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
174  XCTAssertNotNil(engine);
175  XCTAssertNoThrow([engine.binaryMessenger setMessageHandlerOnChannel:@"foo"
176  binaryMessageHandler:nil]);
177 }
178 
179 - (void)testNotifyPluginOfDealloc {
180  id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
181  OCMStub([plugin detachFromEngineForRegistrar:[OCMArg any]]);
182  {
183  FlutterDartProject* project = [[FlutterDartProject alloc] init];
184  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"engine" project:project];
185  NSObject<FlutterPluginRegistrar>* registrar = [engine registrarForPlugin:@"plugin"];
186  [registrar publish:plugin];
187  engine = nil;
188  }
189  OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg any]]);
190 }
191 
192 - (void)testSetBinaryMessengerToSameBinaryMessenger {
193  FakeBinaryMessengerRelay* fakeBinaryMessenger = [[FakeBinaryMessengerRelay alloc] init];
194 
195  FlutterEngine* engine = [[FlutterEngine alloc] init];
196  [engine setBinaryMessenger:fakeBinaryMessenger];
197 
198  // Verify that the setter doesn't free the old messenger before setting the new messenger.
199  fakeBinaryMessenger.failOnDealloc = YES;
200  [engine setBinaryMessenger:fakeBinaryMessenger];
201 
202  // Don't fail when ARC releases the binary messenger.
203  fakeBinaryMessenger.failOnDealloc = NO;
204 }
205 
206 - (void)testRunningInitialRouteSendsNavigationMessage {
207  id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]);
208 
209  FlutterEngine* engine = [[FlutterEngine alloc] init];
210  [engine setBinaryMessenger:mockBinaryMessenger];
211 
212  // Run with an initial route.
213  [engine runWithEntrypoint:FlutterDefaultDartEntrypoint initialRoute:@"test"];
214 
215  // Now check that an encoded method call has been made on the binary messenger to set the
216  // initial route to "test".
217  FlutterMethodCall* setInitialRouteMethodCall =
218  [FlutterMethodCall methodCallWithMethodName:@"setInitialRoute" arguments:@"test"];
219  NSData* encodedSetInitialRouteMethod =
220  [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:setInitialRouteMethodCall];
221  OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/navigation"
222  message:encodedSetInitialRouteMethod]);
223 }
224 
225 - (void)testInitialRouteSettingsSendsNavigationMessage {
226  id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]);
227 
228  auto settings = FLTDefaultSettingsForBundle();
229  settings.route = "test";
230  FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
231  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
232  [engine setBinaryMessenger:mockBinaryMessenger];
233  [engine run];
234 
235  // Now check that an encoded method call has been made on the binary messenger to set the
236  // initial route to "test".
237  FlutterMethodCall* setInitialRouteMethodCall =
238  [FlutterMethodCall methodCallWithMethodName:@"setInitialRoute" arguments:@"test"];
239  NSData* encodedSetInitialRouteMethod =
240  [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:setInitialRouteMethodCall];
241  OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/navigation"
242  message:encodedSetInitialRouteMethod]);
243 }
244 
245 - (void)testPlatformViewsControllerRenderingMetalBackend {
246  FlutterEngine* engine = [[FlutterEngine alloc] init];
247  [engine run];
248  flutter::IOSRenderingAPI renderingApi = [engine platformViewsRenderingAPI];
249 
250  XCTAssertEqual(renderingApi, flutter::IOSRenderingAPI::kMetal);
251 }
252 
253 - (void)testWaitForFirstFrameTimeout {
254  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
255  [engine run];
256  XCTestExpectation* timeoutFirstFrame = [self expectationWithDescription:@"timeoutFirstFrame"];
257  [engine waitForFirstFrame:0.1
258  callback:^(BOOL didTimeout) {
259  if (timeoutFirstFrame) {
260  [timeoutFirstFrame fulfill];
261  }
262  }];
263  [self waitForExpectations:@[ timeoutFirstFrame ]];
264 }
265 
266 - (void)testSpawn {
267  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
268  [engine run];
269  FlutterEngine* spawn = [engine spawnWithEntrypoint:nil
270  libraryURI:nil
271  initialRoute:nil
272  entrypointArgs:nil];
273  XCTAssertNotNil(spawn);
274 }
275 
276 - (void)testSetHandlerAfterRun {
277  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
278  XCTestExpectation* gotMessage = [self expectationWithDescription:@"gotMessage"];
279  dispatch_async(dispatch_get_main_queue(), ^{
280  NSObject<FlutterPluginRegistrar>* registrar = [engine registrarForPlugin:@"foo"];
281  fml::AutoResetWaitableEvent latch;
282  [engine run];
283  flutter::Shell& shell = engine.shell;
284  fml::TaskRunner::RunNowOrPostTask(
285  engine.shell.GetTaskRunners().GetUITaskRunner(), [&latch, &shell] {
286  flutter::Engine::Delegate& delegate = shell;
287  auto message = std::make_unique<flutter::PlatformMessage>("foo", nullptr);
288  delegate.OnEngineHandlePlatformMessage(std::move(message));
289  latch.Signal();
290  });
291  latch.Wait();
292  [registrar.messenger setMessageHandlerOnChannel:@"foo"
293  binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
294  [gotMessage fulfill];
295  }];
296  });
297  [self waitForExpectations:@[ gotMessage ]];
298 }
299 
300 - (void)testThreadPrioritySetCorrectly {
301  XCTestExpectation* prioritiesSet = [self expectationWithDescription:@"prioritiesSet"];
302  prioritiesSet.expectedFulfillmentCount = 2;
303 
304  IMP mockSetThreadPriority =
305  imp_implementationWithBlock(^(NSThread* thread, double threadPriority) {
306  if ([thread.name hasSuffix:@".raster"]) {
307  XCTAssertEqual(threadPriority, 1.0);
308  [prioritiesSet fulfill];
309  } else if ([thread.name hasSuffix:@".io"]) {
310  XCTAssertEqual(threadPriority, 0.5);
311  [prioritiesSet fulfill];
312  }
313  });
314  Method method = class_getInstanceMethod([NSThread class], @selector(setThreadPriority:));
315  IMP originalSetThreadPriority = method_getImplementation(method);
316  method_setImplementation(method, mockSetThreadPriority);
317 
318  FlutterEngine* engine = [[FlutterEngine alloc] init];
319  [engine run];
320  [self waitForExpectations:@[ prioritiesSet ]];
321 
322  method_setImplementation(method, originalSetThreadPriority);
323 }
324 
325 - (void)testCanEnableDisableEmbedderAPIThroughInfoPlist {
326  {
327  // Not enable embedder API by default
328  auto settings = FLTDefaultSettingsForBundle();
329  settings.enable_software_rendering = true;
330  FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
331  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
332  XCTAssertFalse(engine.enableEmbedderAPI);
333  }
334  {
335  // Enable embedder api
336  id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
337  OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableIOSEmbedderAPI"])
338  .andReturn(@"YES");
339  auto settings = FLTDefaultSettingsForBundle();
340  settings.enable_software_rendering = true;
341  FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
342  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
343  XCTAssertTrue(engine.enableEmbedderAPI);
344  }
345 }
346 
347 - (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed {
348  id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]);
349  FlutterEngine* engine = [[FlutterEngine alloc] init];
350  [engine setBinaryMessenger:mockBinaryMessenger];
351  [engine runWithEntrypoint:FlutterDefaultDartEntrypoint initialRoute:@"test"];
352  [engine flutterTextInputView:nil didResignFirstResponderWithTextInputClient:1];
353  FlutterMethodCall* methodCall =
354  [FlutterMethodCall methodCallWithMethodName:@"TextInputClient.onConnectionClosed"
355  arguments:@[ @(1) ]];
356  NSData* encodedMethodCall = [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:methodCall];
357  OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/textinput" message:encodedMethodCall]);
358 }
359 
360 - (void)testFlutterEngineUpdatesDisplays {
361  FlutterEngine* engine = [[FlutterEngine alloc] init];
362  id mockEngine = OCMPartialMock(engine);
363 
364  [engine run];
365  OCMVerify(times(1), [mockEngine updateDisplays]);
366  engine.viewController = nil;
367  OCMVerify(times(2), [mockEngine updateDisplays]);
368 }
369 
370 - (void)testLifeCycleNotificationDidEnterBackground {
371  FlutterDartProject* project = [[FlutterDartProject alloc] init];
372  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
373  [engine run];
374  NSNotification* sceneNotification =
375  [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
376  object:nil
377  userInfo:nil];
378  NSNotification* applicationNotification =
379  [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
380  object:nil
381  userInfo:nil];
382  id mockEngine = OCMPartialMock(engine);
383  [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
384  [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
385 #if APPLICATION_EXTENSION_API_ONLY
386  OCMVerify(times(1), [mockEngine sceneDidEnterBackground:[OCMArg any]]);
387 #else
388  OCMVerify(times(1), [mockEngine applicationDidEnterBackground:[OCMArg any]]);
389 #endif
390  XCTAssertTrue(engine.isGpuDisabled);
391  bool switch_value = false;
392  [engine shell].GetIsGpuDisabledSyncSwitch()->Execute(
393  fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value = true; }).SetIfFalse([&] {
394  switch_value = false;
395  }));
396  XCTAssertTrue(switch_value);
397 }
398 
399 - (void)testLifeCycleNotificationWillEnterForeground {
400  FlutterDartProject* project = [[FlutterDartProject alloc] init];
401  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
402  [engine run];
403  NSNotification* sceneNotification =
404  [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
405  object:nil
406  userInfo:nil];
407  NSNotification* applicationNotification =
408  [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
409  object:nil
410  userInfo:nil];
411  id mockEngine = OCMPartialMock(engine);
412  [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
413  [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
414 #if APPLICATION_EXTENSION_API_ONLY
415  OCMVerify(times(1), [mockEngine sceneWillEnterForeground:[OCMArg any]]);
416 #else
417  OCMVerify(times(1), [mockEngine applicationWillEnterForeground:[OCMArg any]]);
418 #endif
419  XCTAssertFalse(engine.isGpuDisabled);
420  bool switch_value = true;
421  [engine shell].GetIsGpuDisabledSyncSwitch()->Execute(
422  fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value = true; }).SetIfFalse([&] {
423  switch_value = false;
424  }));
425  XCTAssertFalse(switch_value);
426 }
427 
428 - (void)testSpawnsShareGpuContext {
429  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
430  [engine run];
431  FlutterEngine* spawn = [engine spawnWithEntrypoint:nil
432  libraryURI:nil
433  initialRoute:nil
434  entrypointArgs:nil];
435  XCTAssertNotNil(spawn);
436  XCTAssertTrue(engine.platformView != nullptr);
437  XCTAssertTrue(spawn.platformView != nullptr);
438  std::shared_ptr<flutter::IOSContext> engine_context = engine.platformView->GetIosContext();
439  std::shared_ptr<flutter::IOSContext> spawn_context = spawn.platformView->GetIosContext();
440  XCTAssertEqual(engine_context, spawn_context);
441 }
442 
443 - (void)testEnableSemanticsWhenFlutterViewAccessibilityDidCall {
444  FlutterEngineSpy* engine = [[FlutterEngineSpy alloc] initWithName:@"foobar"];
445  engine.ensureSemanticsEnabledCalled = NO;
446  [engine flutterViewAccessibilityDidCall];
447  XCTAssertTrue(engine.ensureSemanticsEnabledCalled);
448 }
449 
450 - (void)testCanMergePlatformAndUIThread {
451 #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
452  auto settings = FLTDefaultSettingsForBundle();
453  FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
454  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
455  [engine run];
456 
457  XCTAssertEqual(engine.shell.GetTaskRunners().GetUITaskRunner(),
458  engine.shell.GetTaskRunners().GetPlatformTaskRunner());
459 #endif // defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
460 }
461 
462 - (void)testCanUnMergePlatformAndUIThread {
463 #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
464  auto settings = FLTDefaultSettingsForBundle();
465  settings.merged_platform_ui_thread = false;
466  FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
467  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
468  [engine run];
469 
470  XCTAssertNotEqual(engine.shell.GetTaskRunners().GetUITaskRunner(),
471  engine.shell.GetTaskRunners().GetPlatformTaskRunner());
472 #endif // defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
473 }
474 
475 @end
FlutterEngine
Definition: FlutterEngine.h:61
FlutterPlugin-p
Definition: FlutterPlugin.h:189
FlutterTextInputDelegate-p
Definition: FlutterTextInputDelegate.h:37
-[FlutterEngine waitForFirstFrame:callback:]
void waitForFirstFrame:callback:(NSTimeInterval timeout,[callback] void(^ callback)(BOOL didTimeout))
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
-[FlutterEngine platformViewsRenderingAPI]
flutter::IOSRenderingAPI platformViewsRenderingAPI()
FlutterEngineSpy::ensureSemanticsEnabledCalled
BOOL ensureSemanticsEnabledCalled
Definition: FlutterEngineTest.mm:24
FlutterEngine_Test.h
-[FlutterEngine ensureSemanticsEnabled]
void ensureSemanticsEnabled()
Definition: FlutterEngine.mm:438
FlutterTextInputPlugin.h
FlutterEngine_Internal.h
-[FlutterEngine(Test) shell]
flutter::Shell & shell()
FlutterEngineSpy
Definition: FlutterEngineTest.mm:23
FlutterMacros.h
-[FlutterEngine setBinaryMessenger:]
void setBinaryMessenger:(FlutterBinaryMessengerRelay *binaryMessenger)
-[FlutterEngine(Test) updateDisplays]
void updateDisplays()
flutter::PlatformViewIOS::GetIosContext
const std::shared_ptr< IOSContext > & GetIosContext()
Definition: platform_view_ios.h:123
-[FlutterEngine shell]
flutter::Shell & shell()
FlutterBinaryMessengerRelay.h
FakeBinaryMessengerRelay::failOnDealloc
BOOL failOnDealloc
Definition: FlutterEngineTest.mm:44
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterEngineTest
Definition: FlutterEngineTest.mm:55
flutter::IOSRenderingAPI
IOSRenderingAPI
Definition: rendering_api_selection.h:14
-[FlutterPluginRegistry-p registrarForPlugin:]
nullable NSObject< FlutterPluginRegistrar > * registrarForPlugin:(NSString *pluginKey)
flutter::IOSRenderingAPI::kMetal
@ kMetal
FakeBinaryMessengerRelay
Definition: FlutterEngineTest.mm:43
engine
id engine
Definition: FlutterTextInputPluginTest.mm:89
FlutterDartProject_Internal.h
-[FlutterEngine platformView]
flutter::PlatformViewIOS * platformView()
FlutterBinaryMessengerRelay
Definition: FlutterBinaryMessengerRelay.h:14
FlutterJSONMethodCodec
Definition: FlutterCodecs.h:455
platform_view_ios.h
FLTDefaultSettingsForBundle
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle, NSProcessInfo *processInfoOrNil)
Definition: FlutterDartProject.mm:47
FlutterDartProject
Definition: FlutterDartProject.mm:252
-[FlutterEngine runWithEntrypoint:initialRoute:]
BOOL runWithEntrypoint:initialRoute:(nullable NSString *entrypoint,[initialRoute] nullable NSString *initialRoute)
-[FlutterEngine spawnWithEntrypoint:libraryURI:initialRoute:entrypointArgs:]
FlutterEngine * spawnWithEntrypoint:libraryURI:initialRoute:entrypointArgs:(/*nullable */NSString *entrypoint,[libraryURI]/*nullable */NSString *libraryURI,[initialRoute]/*nullable */NSString *initialRoute,[entrypointArgs]/*nullable */NSArray< NSString * > *entrypointArgs)
-[FlutterEngine run]
BOOL run()
Definition: FlutterEngine.mm:901
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)