Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions common/config.gni
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ if (is_ios || is_mac) {
flutter_cflags_objc = [
"-Werror=overriding-method-mismatch",
"-Werror=undeclared-selector",
"-fapplication-extension",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This would be set by a buildroot flag so the engine can be built with or without app extension safety. I put it here for testing.

]
flutter_cflags_objcc = flutter_cflags_objc
flutter_cflags_objc_arc = flutter_cflags_objc + [ "-fobjc-arc" ]
Expand Down
15 changes: 12 additions & 3 deletions shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ source_set("flutter_framework_source_arc") {
cflags_objc = flutter_cflags_objc_arc
cflags_objcc = flutter_cflags_objcc_arc

defines = [ "FLUTTER_FRAMEWORK=1" ]
defines = [
"FLUTTER_FRAMEWORK=1",
"APPLICATION_EXTENSION_API_ONLY=1",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same, this would be set by a buildroot flag.

]
allow_circular_includes_from = [ ":flutter_framework_source" ]
deps = [
":flutter_framework_source",
Expand Down Expand Up @@ -153,7 +156,10 @@ source_set("flutter_framework_source") {

sources += _flutter_framework_headers

defines = [ "FLUTTER_FRAMEWORK=1" ]
defines = [
"FLUTTER_FRAMEWORK=1",
"APPLICATION_EXTENSION_API_ONLY=1",
]

if (shell_enable_metal) {
sources += [
Expand Down Expand Up @@ -320,7 +326,10 @@ shared_library("create_flutter_framework_dylib") {

output_name = "Flutter"

ldflags = [ "-Wl,-install_name,@rpath/Flutter.framework/Flutter" ]
ldflags = [
"-Wl,-install_name,@rpath/Flutter.framework/Flutter",
"-fapplication-extension",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same, behind a new flag, etc.

]

public = _flutter_framework_headers

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* code as necessary from FlutterAppDelegate.mm.
*/
FLUTTER_DARWIN_EXPORT
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions")
@interface FlutterAppDelegate
: UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>

Expand Down
3 changes: 2 additions & 1 deletion shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ typedef enum {
*
* @param delegate The receiving object, such as the plugin's main class.
*/
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate;
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions");

/**
* Returns the file name for the given asset.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
* Propagates `UIAppDelegate` callbacks to registered plugins.
*/
FLUTTER_DARWIN_EXPORT
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions")
@interface FlutterPluginAppLifeCycleDelegate : NSObject <UNUserNotificationCenterDelegate>

/**
Expand Down
6 changes: 5 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,11 @@ - (BOOL)createShell:(NSString*)entrypoint
);

_isGpuDisabled =
#if APPLICATION_EXTENSION_API_ONLY
NO;
#else
[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 @@ -1433,7 +1437,7 @@ - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
}];
}

- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate NS_EXTENSION_UNAVAILABLE_IOS("") {
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
id<FlutterAppLifeCycleProvider> lifeCycleProvider =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ - (void)setSystemChromeApplicationSwitcherDescription:(NSDictionary*)object {
}

- (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays {
#if !APPLICATION_EXTENSION_API_ONLY
// Checks if the top status bar should be visible. This platform ignores all
// other overlays

Expand All @@ -175,9 +176,11 @@ - (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays {
postNotificationName:FlutterViewControllerHideHomeIndicator
object:nil];
}
#endif
}

- (void)setSystemChromeEnabledSystemUIMode:(NSString*)mode {
#if !APPLICATION_EXTENSION_API_ONLY
// Checks if the top status bar should be visible, reflected by edge to edge setting. This
// platform ignores all other system ui modes.

Expand All @@ -195,6 +198,7 @@ - (void)setSystemChromeEnabledSystemUIMode:(NSString*)mode {
postNotificationName:FlutterViewControllerHideHomeIndicator
object:nil];
}
#endif
}

- (void)restoreSystemChromeSystemUIOverlays {
Expand Down Expand Up @@ -231,9 +235,11 @@ - (void)setSystemChromeSystemUIOverlayStyle:(NSDictionary*)message {
object:nil
userInfo:@{@(kOverlayStyleUpdateNotificationKey) : @(statusBarStyle)}];
} else {
#if !APPLICATION_EXTENSION_API_ONLY
// Note: -[UIApplication setStatusBarStyle] is deprecated in iOS9
// in favor of delegating to the view controller
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle];
#endif
}
}

Expand All @@ -249,11 +255,15 @@ - (void)popSystemNavigator:(BOOL)isAnimated {
if (navigationController) {
[navigationController popViewControllerAnimated:isAnimated];
} else {
#if !APPLICATION_EXTENSION_API_ONLY
UIViewController* rootViewController =
[UIApplication sharedApplication].keyWindow.rootViewController;
if (engineViewController != rootViewController) {
#endif
[engineViewController dismissViewControllerAnimated:isAnimated completion:nil];
#if !APPLICATION_EXTENSION_API_ONLY
}
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,9 +814,14 @@ - (void)viewDidAppear:(BOOL)animated {
if ([_engine.get() viewController] == self) {
[self onUserSettingsChanged:nil];
[self onAccessibilityStatusChanged:nil];

#if !APPLICATION_EXTENSION_API_ONLY
if (UIApplication.sharedApplication.applicationState == UIApplicationStateActive) {
#endif
[[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it possible to end up sending this if the whole process hosting the extension itself was in the background, thus UIApplication.sharedApplication.applicationState != UIApplicationStateActive if we had access to the sharedApplication? If we sent in "resumed" without checking, might be end up in an engine state where we continue to issue GPU commands while in the background and crash?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Seems like a race that I bet could happen in the app extension #23175

#if !APPLICATION_EXTENSION_API_ONLY
}
#endif
}
[super viewDidAppear:animated];
}
Expand Down Expand Up @@ -1304,8 +1309,13 @@ - (void)viewDidLayoutSubviews {
// There is no guarantee that UIKit will layout subviews when the application is active. Creating
// the surface when inactive will cause GPU accesses from the background. Only wait for the first
// frame to render when the application is actually active.
bool applicationIsActive =
BOOL applicationIsActive =

#if APPLICATION_EXTENSION_API_ONLY
YES;
#else
[UIApplication sharedApplication].applicationState == UIApplicationStateActive;
#endif

// This must run after updateViewportMetrics so that the surface creation tasks are queued after
// the viewport metrics update tasks.
Expand Down Expand Up @@ -1804,6 +1814,7 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification {
}

- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
#if !APPLICATION_EXTENSION_API_ONLY

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Might be able to fall back to changing UIWindowSceneGeometryPreferencesIOS on self.viewIfLoaded.window.windowScene instead, if that works.

if (new_preferences != _orientationPreferences) {
_orientationPreferences = new_preferences;

Expand Down Expand Up @@ -1850,6 +1861,7 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
}
}
}
#endif
}

- (void)onHideHomeIndicatorNotification:(NSNotification*)notification {
Expand Down Expand Up @@ -1951,7 +1963,12 @@ - (void)onUserSettingsChanged:(NSNotification*)notification {
}

- (CGFloat)textScaleFactor {
#if APPLICATION_EXTENSION_API_ONLY
UIContentSizeCategory category =
self.mainScreenIfViewLoaded.traitCollection.preferredContentSizeCategory;
#else
UIContentSizeCategory category = [UIApplication sharedApplication].preferredContentSizeCategory;
#endif
// The delta is computed by approximating Apple's typography guidelines:
// https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/
//
Expand Down