Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions ci/builders/mac_unopt.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,77 @@
"script": "flutter/testing/scenario_app/run_ios_tests.sh"
}

]
},
{
"archives": [
Copy link
Contributor Author

@cyanglaz cyanglaz Jul 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since intel machines are limited and the test suite are the same, only add Apple silicon machine for the ci tests.

{
"base_path": "out/ios_debug_sim_arm64_extension_safe/zip_archives/",
"type": "gcs",
"include_paths": [
],
"name": "ios_debug_sim_arm64_extension_safe"
}
],
"properties": {
"$flutter/osx_sdk": {
"runtime_versions": [
"ios-16-4_14e300c",
"ios-16-2_14c18"
],
"sdk_version": "14e300c"
}
},
"drone_dimensions": [
"device_type=none",
"os=Mac-12",
"cpu=arm64"
],
"gclient_variables": {
"download_android_deps": false
},
"gn": [
"--ios",
"--runtime-mode",
"debug",
"--simulator",
"--no-lto",
"--force-mac-arm64",
"--simulator-cpu",
"arm64",
"--darwin-extension-safe"
],
"name": "ios_debug_sim_arm64_extension_safe",
"ninja": {
"config": "ios_debug_sim_arm64_extension_safe",
"targets": [
"flutter/testing/scenario_app",
"flutter/shell/platform/darwin/ios:ios_test_flutter"
]
},
"tests": [
{
"language": "python3",
"name": "Tests for ios_debug_sim_arm64_extension_safe",
"parameters": [
"--variant",
"ios_debug_sim_arm64_extension_safe",
"--type",
"objc",
"--engine-capture-core-dump",
"--ios-variant",
"ios_debug_sim_arm64_extension_safe"
],
"script": "flutter/testing/run_tests.py"
},
{
"name": "Scenario App Integration Tests",
"parameters": [
"ios_debug_sim_arm64_extension_safe"
],
"script": "flutter/testing/scenario_app/run_ios_tests.sh"
}

]
}
]
Expand Down
6 changes: 6 additions & 0 deletions common/config.gni
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ declare_args() {

# Whether to include backtrace support.
enable_backtrace = true

# Whether to include --fapplication-extension when build iOS framework.
# This is currently a test flag and does not work properly.
#TODO(cyanglaz): Remove above comment about test flag when the entire iOS embedder supports app extension
#https://github.com/flutter/flutter/issues/124289
darwin_extension_safe = false
}

# feature_defines_list ---------------------------------------------------------
Expand Down
14 changes: 14 additions & 0 deletions shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ source_set("flutter_framework_source_arc") {
cflags_objcc = flutter_cflags_objcc_arc

defines = [ "FLUTTER_FRAMEWORK=1" ]
if (darwin_extension_safe) {
defines += [ "APPLICATION_EXTENSION_API_ONLY=1" ]
}
allow_circular_includes_from = [ ":flutter_framework_source" ]
deps = [
":flutter_framework_source",
Expand Down Expand Up @@ -153,6 +156,9 @@ source_set("flutter_framework_source") {
sources += _flutter_framework_headers

defines = [ "FLUTTER_FRAMEWORK=1" ]
if (darwin_extension_safe) {
defines += [ "APPLICATION_EXTENSION_API_ONLY=1" ]
}

if (shell_enable_metal) {
sources += [
Expand Down Expand Up @@ -249,6 +255,10 @@ source_set("ios_test_flutter_mrc") {
if (shell_enable_vulkan) {
deps += [ "//flutter/vulkan" ]
}

if (darwin_extension_safe) {
defines = [ "APPLICATION_EXTENSION_API_ONLY=1" ]
}
}

shared_library("ios_test_flutter") {
Expand Down Expand Up @@ -312,6 +322,10 @@ shared_library("ios_test_flutter") {
":ios_gpu_configuration_config",
"//flutter:config",
]

if (darwin_extension_safe) {
defines = [ "APPLICATION_EXTENSION_API_ONLY=1" ]
}
}

shared_library("create_flutter_framework_dylib") {
Expand Down
70 changes: 61 additions & 9 deletions shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -225,22 +225,44 @@ - (instancetype)initWithName:(NSString*)labelPrefix
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];

#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 13.0, *)) {
[self setUpSceneLifecycleNotifications:center];
} else {
[self setUpApplicationLifecycleNotifications:center];
}
#else
[self setUpApplicationLifecycleNotifications:center];
#endif

[center addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
selector:@selector(onLocaleUpdated:)
name:NSCurrentLocaleDidChangeNotification
object:nil];

return self;
}

- (void)setUpSceneLifecycleNotifications:(NSNotificationCenter*)center API_AVAILABLE(ios(13.0)) {
[center addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
selector:@selector(sceneWillEnterForeground:)
name:UISceneWillEnterForegroundNotification
object:nil];

[center addObserver:self
selector:@selector(onLocaleUpdated:)
name:NSCurrentLocaleDidChangeNotification
selector:@selector(sceneDidEnterBackground:)
name:UISceneDidEnterBackgroundNotification
object:nil];
}

return self;
- (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center {
[center addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[center addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}

- (void)recreatePlatformViewController {
Expand Down Expand Up @@ -856,8 +878,20 @@ - (BOOL)createShell:(NSString*)entrypoint
_threadHost->io_thread->GetTaskRunner() // io
);

#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 13.0, *)) {
_isGpuDisabled = self.viewController.windowSceneIfViewLoaded.activationState ==
UISceneActivationStateBackground;
} else {
// [UIApplication sharedApplication API is not available for app extension.
// We intialize the shell assuming the GPU is required.
_isGpuDisabled = NO;
}
#else
_isGpuDisabled =
[UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
#endif

// Create the shell. This is a blocking operation.
std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
/*platform_data=*/platformData,
Expand Down Expand Up @@ -1302,11 +1336,29 @@ - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {

#pragma mark - Notifications

#if APPLICATION_EXTENSION_API_ONLY
- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
[self flutterWillEnterForeground:notification];
}

- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
[self flutterDidEnterBackground:notification];
}
#else
- (void)applicationWillEnterForeground:(NSNotification*)notification {
[self setIsGpuDisabled:NO];
[self flutterWillEnterForeground:notification];
}

- (void)applicationDidEnterBackground:(NSNotification*)notification {
[self flutterDidEnterBackground:notification];
}
#endif

- (void)flutterWillEnterForeground:(NSNotification*)notification {
[self setIsGpuDisabled:NO];
}

- (void)flutterDidEnterBackground:(NSNotification*)notification {
[self setIsGpuDisabled:YES];
[self notifyLowMemory];
}
Expand Down
59 changes: 59 additions & 0 deletions shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import <objc/runtime.h>

#import "flutter/common/settings.h"
#include "flutter/fml/synchronization/sync_switch.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
Expand Down Expand Up @@ -341,4 +342,62 @@ - (void)testFlutterEngineUpdatesDisplays {
OCMVerify(times(2), [mockEngine updateDisplays]);
}

- (void)testLifeCycleNotificationDidEnterBackground {
FlutterDartProject* project = [[FlutterDartProject alloc] init];
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
[engine run];
NSNotification* sceneNotification =
[NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
object:nil
userInfo:nil];
NSNotification* applicationNotification =
[NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
object:nil
userInfo:nil];
id mockEngine = OCMPartialMock(engine);
[[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
[[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
#if APPLICATION_EXTENSION_API_ONLY
OCMVerify(times(1), [mockEngine sceneDidEnterBackground:[OCMArg any]]);
#else
OCMVerify(times(1), [mockEngine applicationDidEnterBackground:[OCMArg any]]);
#endif
XCTAssertTrue(engine.isGpuDisabled);
bool switch_value = false;
[engine shell].GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value = true; }).SetIfFalse([&] {
switch_value = false;
}));
XCTAssertTrue(switch_value);
}

- (void)testLifeCycleNotificationWillEnterForeground {
FlutterDartProject* project = [[FlutterDartProject alloc] init];
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
[engine run];
NSNotification* sceneNotification =
[NSNotification notificationWithName:UISceneWillEnterForegroundNotification
object:nil
userInfo:nil];
NSNotification* applicationNotification =
[NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
object:nil
userInfo:nil];
id mockEngine = OCMPartialMock(engine);
[[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
[[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
#if APPLICATION_EXTENSION_API_ONLY
OCMVerify(times(1), [mockEngine sceneWillEnterForeground:[OCMArg any]]);
#else
OCMVerify(times(1), [mockEngine applicationWillEnterForeground:[OCMArg any]]);
#endif
XCTAssertFalse(engine.isGpuDisabled);
bool switch_value = true;
[engine shell].GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value = true; }).SetIfFalse([&] {
switch_value = false;
}));
XCTAssertFalse(switch_value);
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ class ThreadHost;
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
performAction:(FlutterTextInputAction)action
withClient:(int)client;
- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
- (void)applicationWillEnterForeground:(NSNotification*)notification;
- (void)applicationDidEnterBackground:(NSNotification*)notification;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,23 @@ - (void)popSystemNavigator:(BOOL)isAnimated {
// It's also possible in an Add2App scenario that the FlutterViewController was presented
// outside the context of a UINavigationController, and still wants to be popped.

UIViewController* engineViewController = [_engine.get() viewController];
FlutterViewController* engineViewController = [_engine.get() viewController];
UINavigationController* navigationController = [engineViewController navigationController];
if (navigationController) {
[navigationController popViewControllerAnimated:isAnimated];
} else {
UIViewController* rootViewController =
[UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController* rootViewController = nil;
#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 15.0, *)) {
rootViewController =
[engineViewController windowSceneIfViewLoaded].keyWindow.rootViewController;
} else {
FML_LOG(WARNING)
<< "rootViewController is not available in application extension prior to iOS 15.0.";
}
#else
rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
#endif
if (engineViewController != rootViewController) {
[engineViewController dismissViewControllerAnimated:isAnimated completion:nil];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ - (void)testLookUpCallInitiated {
XCTestExpectation* presentExpectation =
[self expectationWithDescription:@"Look Up view controller presented"];

FlutterViewController* engineViewController = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
FlutterViewController* engineViewController =
[[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil] autorelease];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The view controller is leaked, causing the newly added notification tests in other testing files to fail.

FlutterViewController* mockEngineViewController = OCMPartialMock(engineViewController);

FlutterPlatformPlugin* plugin =
Expand Down
Loading