From 0d2debf05460d456d2afbcd25001d5d40edaec6a Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 15 Apr 2021 09:17:00 +0200 Subject: [PATCH 1/4] XCUITest to verify iOS quick_actions in cold start --- .../example/ios/RunnerUITests/RunnerUITests.m | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) 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 45791864d239..9991e344fe91 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 @@ - (void)setUp { 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 @@ - (void)testQuickAction { @(kElementWaitingTime)); } XCTAssertTrue(actionOneConfirmation.exists); + + [app terminate]; } @end From 2bf782efa3fccb489e6bf20ed658aa23c88a4626 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 16 Apr 2021 13:27:25 +0200 Subject: [PATCH 2/4] Handle complete iOS lifecycle --- .../quick_actions/quick_actions/CHANGELOG.md | 4 +++ .../example/ios/Runner/AppDelegate.m | 4 +-- .../ios/Classes/FLTQuickActionsPlugin.m | 30 +++++++++++++++++-- .../quick_actions/quick_actions/pubspec.yaml | 2 +- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 3fd0c7e28256..f849d90bd902 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 30b87969f44a..a89d86c28c6f 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 @@ - (BOOL)application:(UIApplication *)application 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/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m index 0025795744f9..9f72027c09f2 100644 --- a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m @@ -10,7 +10,9 @@ @interface FLTQuickActionsPlugin () @property(nonatomic, retain) FlutterMethodChannel *channel; @end -@implementation FLTQuickActionsPlugin +@implementation FLTQuickActionsPlugin { + NSString *shortcutType; +} + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = @@ -50,12 +52,36 @@ - (BOOL)application:(UIApplication *)application 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) { + shortcutType = shortcutItem.type; + return NO; + } + } + return YES; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if (shortcutType) { + [self _handleShortcut:shortcutType]; + 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 *newShortcuts = [[NSMutableArray alloc] init]; diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index f6622525d458..2bcdcf7ea8a5 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions 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: From 49067dc692a0ebead77c45f497f8023e1fb88420 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Sat, 17 Apr 2021 09:10:40 +0200 Subject: [PATCH 3/4] Processed feedback on PR --- .../ios/Classes/FLTQuickActionsPlugin.m | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m index 9f72027c09f2..61752fe129cb 100644 --- a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m @@ -8,11 +8,10 @@ @interface FLTQuickActionsPlugin () @property(nonatomic, retain) FlutterMethodChannel *channel; +@property(nonatomic, retain) NSString *shortcutType; @end -@implementation FLTQuickActionsPlugin { - NSString *shortcutType; -} +@implementation FLTQuickActionsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = @@ -52,7 +51,7 @@ - (BOOL)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler API_AVAILABLE(ios(9.0)) { - [self _handleShortcut:shortcutItem.type]; + [self handleShortcut:shortcutItem.type]; return YES; } @@ -62,7 +61,7 @@ - (BOOL)application:(UIApplication *)application UIApplicationShortcutItem *shortcutItem = launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; if (shortcutItem) { - shortcutType = shortcutItem.type; + self.shortcutType = shortcutItem.type; return NO; } } @@ -70,15 +69,15 @@ - (BOOL)application:(UIApplication *)application } - (void)applicationDidBecomeActive:(UIApplication *)application { - if (shortcutType) { - [self _handleShortcut:shortcutType]; - shortcutType = nil; + if (self.shortcutType) { + [self handleShortcut:self.shortcutType]; + self.shortcutType = nil; } } #pragma mark Private functions -- (void)_handleShortcut:(NSString *)shortcut { +- (void)handleShortcut:(NSString *)shortcut { [self.channel invokeMethod:@"launch" arguments:shortcut]; } From 8b38c46d7a64870265a222598c5a38fdfb2eec5c Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Sun, 18 Apr 2021 21:40:04 +0200 Subject: [PATCH 4/4] Added comments to explain code --- .../quick_actions/ios/Classes/FLTQuickActionsPlugin.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m index 61752fe129cb..3a966f86a824 100644 --- a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m @@ -61,7 +61,15 @@ - (BOOL)application:(UIApplication *)application 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; } }