flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.38k stars 27.29k forks source link

Quick Action prevents other plugins from receiving call to didFinishLaunchingWithOptions #141280

Open flodaniel opened 9 months ago

flodaniel commented 9 months ago

What package does this bug report belong to?

quick_actions

What target platforms are you seeing this bug on?

iOS

Have you already upgraded your packages?

Yes

Dependency versions

pubspec.lock (Link to pastebin because too long)

Steps to reproduce

  1. Install quick_actions and any other plugin that also relies on on the iOS lifecycle hook didFinishLaunchingWithOptions from being called
  2. Use a quick action

Expected results

I would expect that both plugins didFinishLaunchingWithOptions swift method would be called.

Actual results

didFinishLaunchingWithOptions in the QuickActionsPlugin.swift is called. didFinishLaunchingWithOptions in my other plugin is NOT called.

This is because QuickActionsPlugin.swift returns false, if it reacts to a quick action. I suspect that also the order matters, of how the plugins are included in GeneratedPluginRegistrant.m. In my case the other plugin is included after the QuickActions plugin.

Code sample

Problem code ```swift public func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable: Any] = [:] ) -> Bool { if let shortcutItem = launchOptions[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem { // Keep hold of the shortcut type and handle it in the // `applicationDidBecomeActive:` method once the Dart MethodChannel // is initialized. launchingShortcutType = shortcutItem.type // Return false 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). return false } return true } ```
Changing to "return true" fixes it in my testing ```swift public func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable: Any] = [:] ) -> Bool { if let shortcutItem = launchOptions[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem { // Keep hold of the shortcut type and handle it in the // `applicationDidBecomeActive:` method once the Dart MethodChannel // is initialized. launchingShortcutType = shortcutItem.type // Return false 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). return true } return true } ```

Screenshots or Videos

Screenshots / Video demonstration [Upload media here]

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale en-AT) • Flutter version 3.16.5 on channel stable at /Users/floriandaniel/Documents/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 78666c8dc5 (3 weeks ago), 2023-12-19 16:14:14 -0800 • Engine revision 3f3e560236 • Dart version 3.2.3 • DevTools version 2.28.4 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/floriandaniel/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • ANDROID_HOME = /Users/floriandaniel/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.2) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 15C500b • CocoaPods version 1.12.1 [✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable. [✓] Android Studio (version 2023.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) [✓] IntelliJ IDEA Community Edition (version 2023.2.2) • IntelliJ at /Applications/IntelliJ IDEA CE.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.85.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.80.0 [✓] Connected device (2 available) • iPhone SE (3rd generation) (mobile) • 0E041D4D-0E7A-4C38-9BA3-3B61FB968B10 • ios • com.apple.CoreSimulator.SimRuntime.iOS-17-2 (simulator) • macOS (desktop) • macos • darwin-arm64 • macOS 14.1.2 23B92 darwin-arm64 ! Error: Browsing on the local area network for Gernot Panholzer’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) ! Error: Browsing on the local area network for Backup iPhone von Arthur. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) ! Error: Browsing on the local area network for Gernot’s Apple Watch. Ensure the device is unlocked and discoverable via Bluetooth. (code -27) [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ```
darshankawar commented 9 months ago

Thanks for the report @flodaniel

Changing to "return true" fixes it in my testing

The flag is set to false with reasoning as mentioned in the comments:

// Return false 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).
      return false

didFinishLaunchingWithOptions in my other plugin is NOT called.

Can you provide the plugin you are using ? Not sure if this would be in scope of quick_actions plugin to set it to true or need to be handled by the plugin owner itself.

flodaniel commented 9 months ago

Hi @darshankawar,

the plugin is a wrapper around the Sentiance iOS sdk (i can't share it fully, as it is internal), which requires its initialization to be done in didFinishLaunchingWithOptions: https://docs.sentiance.com/a-complete-integration/ios-sdk/initialization#id-3.-initialize-the-sdk

image

I can't see how I could resolve this in my plugin, if the app is started through a quick action.

It is correct that the iOS docs explictly states that return false is correct for quick_actions. However in a native iOS application we never have multiple didFinishLaunchingWithOptions methods that cannot be merged into one. As a general best practice for flutter ios plugins, I think they should always finish with return super.application(application, didFinishLaunchingWithOptions: launchOptions)

Maybe the quick_actions plugin can prevent the duplicate execution by storing the state in an additional boolean flag which is checked in performActionFor?

darshankawar commented 9 months ago

Thanks for the feedback. Keeping the issue open for team's input and tracking.

stuartmorgan commented 8 months ago

Confirming to investigate the use case more broadly. I don't think this is a bug in the plugin based on initial investigation, but: 1) We need to better document the behavior and return value for this delegate method, and 2) We should make sure we are providing a way for plugins to get a lifecycle notification when they don't need to manage the boolean return value, which is probably as a separate API, and we should point to that in the documentation as well.

flodaniel commented 8 months ago

@stuartmorgan thanks for checking in here.

2. We should make sure we are providing a way for plugins to get a lifecycle notification when they don't need to manage the boolean return value, which is probably as a separate API, and we should point to that in the documentation as well.

Is there already a way get a lifecycle notification (for my second plugin)? Do you see any options for a short-term workaround that either make sure the quick_actions plugin does not prevent the lifecyle notification to reach other plugins?

stuartmorgan commented 8 months ago

Is there already a way get a lifecycle notification (for my second plugin)?

You could currently do so the same way the SDK you are wrapping does it: instruct people, in your documentation, to add a call to initialize your plugin from their application's didFinishLaunchingWithOptions.

Do you see any options for a short-term workaround that either make sure the quick_actions plugin does not prevent the lifecyle notification to reach other plugins?

The issue is that it's not a lifecycle notification, it's a delegate method with a return value, where the return value has specific semantics. Your suggestion earlier for changing quick_actions would violate those semantics and make the plugin fail to do what Apple documentation explicitly describes as the correct behavior for handling quick actions.