[quick_actions] handle cold start on iOS correctly (#3811)
diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md
index 3fd0c7e..f849d90 100644
--- a/packages/quick_actions/quick_actions/CHANGELOG.md
+++ b/packages/quick_actions/quick_actions/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.0+1
+
+* Correctly handle iOS Application lifecycle events on cold start of the App.
+
## 0.6.0
* Migrate to federated architecture.
diff --git a/packages/quick_actions/quick_actions/example/ios/Runner/AppDelegate.m b/packages/quick_actions/quick_actions/example/ios/Runner/AppDelegate.m
index 30b8796..a89d86c 100644
--- a/packages/quick_actions/quick_actions/example/ios/Runner/AppDelegate.m
+++ b/packages/quick_actions/quick_actions/example/ios/Runner/AppDelegate.m
@@ -11,7 +11,7 @@
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
- return [super application:application didFinishLaunchingWithOptions:launchOptions];
+ [super application:application didFinishLaunchingWithOptions:launchOptions];
+ return NO;
}
-
@end
diff --git a/packages/quick_actions/quick_actions/example/ios/RunnerUITests/RunnerUITests.m b/packages/quick_actions/quick_actions/example/ios/RunnerUITests/RunnerUITests.m
index 4579186..9991e34 100644
--- a/packages/quick_actions/quick_actions/example/ios/RunnerUITests/RunnerUITests.m
+++ b/packages/quick_actions/quick_actions/example/ios/RunnerUITests/RunnerUITests.m
@@ -18,9 +18,45 @@
self.continueAfterFailure = NO;
}
-- (void)testQuickAction {
+- (void)testQuickActionWithFreshStart {
XCUIApplication *app = [[XCUIApplication alloc] init];
[app launch];
+ [app terminate];
+
+ XCUIApplication *springboard =
+ [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"];
+ XCUIElement *quickActionsAppIcon = springboard.icons[@"quick_actions_example"];
+ if (![quickActionsAppIcon waitForExistenceWithTimeout:kElementWaitingTime]) {
+ os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription);
+ XCTFail(@"Failed due to not able to find the example app from springboard with %@ seconds",
+ @(kElementWaitingTime));
+ }
+
+ [quickActionsAppIcon pressForDuration:2];
+ XCUIElement *actionTwo = springboard.buttons[@"Action two"];
+ if (![actionTwo waitForExistenceWithTimeout:kElementWaitingTime]) {
+ os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription);
+ XCTFail(@"Failed due to not able to find the actionTwo button from springboard with %@ seconds",
+ @(kElementWaitingTime));
+ }
+
+ [actionTwo tap];
+
+ XCUIElement *actionTwoConfirmation = app.otherElements[@"action_two"];
+ if (![actionTwoConfirmation waitForExistenceWithTimeout:kElementWaitingTime]) {
+ os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription);
+ XCTFail(@"Failed due to not able to find the actionTwoConfirmation in the app with %@ seconds",
+ @(kElementWaitingTime));
+ }
+ XCTAssertTrue(actionTwoConfirmation.exists);
+
+ [app terminate];
+}
+
+- (void)testQuickActionWhenAppIsInBackground {
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+ [app launch];
+
XCUIElement *actionsReady = app.otherElements[@"actions ready"];
if (![actionsReady waitForExistenceWithTimeout:kElementWaitingTime]) {
os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
@@ -56,6 +92,8 @@
@(kElementWaitingTime));
}
XCTAssertTrue(actionOneConfirmation.exists);
+
+ [app terminate];
}
@end
diff --git a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m
index 0025795..3a966f8 100644
--- a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m
+++ b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m
@@ -8,6 +8,7 @@
@interface FLTQuickActionsPlugin ()
@property(nonatomic, retain) FlutterMethodChannel *channel;
+@property(nonatomic, retain) NSString *shortcutType;
@end
@implementation FLTQuickActionsPlugin
@@ -50,12 +51,44 @@
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler
API_AVAILABLE(ios(9.0)) {
- [self.channel invokeMethod:@"launch" arguments:shortcutItem.type];
+ [self handleShortcut:shortcutItem.type];
return YES;
}
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ if (@available(iOS 9.0, *)) {
+ UIApplicationShortcutItem *shortcutItem =
+ launchOptions[UIApplicationLaunchOptionsShortcutItemKey];
+ if (shortcutItem) {
+ // Keep hold of the shortcut type and handle it in the
+ // `applicationDidBecomeActure:` method once the Dart MethodChannel
+ // is initialized.
+ self.shortcutType = shortcutItem.type;
+
+ // Return NO to indicate we handled the quick action to ensure
+ // the `application:performActionFor:` method is not called (as
+ // per Apple's documentation:
+ // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622935-application?language=objc).
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ if (self.shortcutType) {
+ [self handleShortcut:self.shortcutType];
+ self.shortcutType = nil;
+ }
+}
+
#pragma mark Private functions
+- (void)handleShortcut:(NSString *)shortcut {
+ [self.channel invokeMethod:@"launch" arguments:shortcut];
+}
+
NS_INLINE void _setShortcutItems(NSArray *items) API_AVAILABLE(ios(9.0)) {
NSMutableArray<UIApplicationShortcutItem *> *newShortcuts = [[NSMutableArray alloc] init];
diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml
index f662252..2bcdcf7 100644
--- a/packages/quick_actions/quick_actions/pubspec.yaml
+++ b/packages/quick_actions/quick_actions/pubspec.yaml
@@ -2,7 +2,7 @@
description: Flutter plugin for creating shortcuts on home screen, also known as
Quick Actions on iOS and App Shortcuts on Android.
homepage: https://github.com/flutter/plugins/tree/master/packages/quick_actions
-version: 0.6.0
+version: 0.6.0+1
flutter:
plugin: