mullvad / mullvadvpn-app

The Mullvad VPN client app for desktop and mobile
https://mullvad.net/
GNU General Public License v3.0
5.12k stars 339 forks source link

Adding support for Siri Shortcuts in iOS #2897

Open rpecka opened 3 years ago

rpecka commented 3 years ago

Issue report

Operating system: iOS 14.6

App version: 2021.2

Issue description

I have a use case where I'd like to be able to enable and disable the VPN on iOS using the native automation solution, Shortcuts. I did some research and testing to see if this could be done in Mullvad and I ran into enough issues that I think it's worth some discussion.

Problems with IntentsExtensions

It looks like the recommended way to support shortcuts is to add an IntentsExtension to your app. This allows for the minimal functionality of your app to be packaged into its own executable and run by the system to execute your app's actions. Keeping the code in a minimal executable helps keep the footprint of the action small and helps the app stay within the 10 second time limit.

I tried implementing an IntentsExtension for Mullvad and ran into this issue. TLDR: VPN configurations in iOS seem like they can only be modified by the executable with the bundle ID that created them. Since the IntentsExtension must have a different bundle ID from the host app, this means that an extension is probably out of the question until this is resolved.

In-app Intent Handling

In iOS 14, support for in-app intent handling was added. This means that intents which are listed in the host app's Info.plist will be forwarded directly to the app rather than to an IntentsExtension (Not sure what the behavior is if the intent is listed in the extension and the host).

I tried this out by implementing the required AppDelegate plumbing but found that none of the delegate functions were being called. This led me to this WWDC session where the speaker outlines that to support in-app intent handling:

  1. Add support for multiple windows using the UIScene lifecycle
  2. Add the list of supported intents to Info.plist
  3. Add the AppDelegate plumbing for intent handling

2 and 3 are easy, 1 is the main reason I'm making this post.

Supporting the UIScene Lifecycle

Supporting the UIScene Lifecycle in the canonical way would require us to remove all UI code from application(_:didFinishLaunchingWithOptions:), refactor the app/account initialization process as it relates to the UI, and make heavy modifications to how we are storing ViewControllers i.e. not storing them in AppDelegate and allowing for more than one of each controller to exist simultaneously.

Alternatively, I believe that there's a way that we can force the app to only allow one window even with the UIScene lifecycle, and use that ability to hack the current architecture into the UIScene lifecycle.

I'm happy contributing either of these solutions, though I would prefer to do the refactor to get it working in the canonical way, but I wanted to see if there were any other ideas on how we could make this work.


An interesting note here is that in-app handling was back ported into iOS 13 using a different AppDelegate call, so we should be able to support this feature in iOS 13 and 14, but not 12.

pronebird commented 3 years ago

Great summary! We came up with identical results while conducting our own research earlier this year.

Sooner or later we will have to migrate to UIScene and rework the application lifecycle. UISceneDelegate methods look and feel similar to UIApplicationDelegate.

Supporting the UIScene Lifecycle in the canonical way would require us to remove all UI code from application(_:didFinishLaunchingWithOptions:), refactor the app/account initialization process as it relates to the UI, and make heavy modifications to how we are storing ViewControllers i.e. not storing them in AppDelegate and allowing for more than one of each controller to exist simultaneously.

Agree.

Alternatively, I believe that there's a way that we can force the app to only allow one window even with the UIScene lifecycle, and use that ability to hack the current architecture into the UIScene lifecycle.

The Info.plist specifies the flag that enables multi-window support (UIApplicationSupportsMultipleScenes), so that's not too terrible to disable it. I don't think there is a benefit of having more than one window of our app. We provide split view on iPad so that should be sufficient enough.

I'm happy contributing either of these solutions, though I would prefer to do the refactor to get it working in the canonical way, but I wanted to see if there were any other ideas on how we could make this work.

Really no other way around but to go with UIScene, this is the future. You're welcome to take a stab at that. It's highly likely that we won't be able to support iOS 12 for a very long time, given the amount of changes in UIKit and other frameworks.

pronebird commented 2 years ago

@rpecka current master already has multi-window support configuration, in-app intents handling is added in https://github.com/mullvad/mullvadvpn-app/commit/446fc2ceb1482ea838eb879db75f6980f026f639. I hope we can ship it sometime soon.

pronebird commented 2 years ago

3807 adds intents to start, stop or reconnect VPN (select next server based on selected location constraints). Can be used via Shortcuts app and subsequently via Siri. This should become available with the next release.

pronebird commented 1 year ago

Re-opening this issue after extensive testing.

In-app intents handling works as intended on iPhone where multi-window support does not seem to provide users with ability to open new windows or close existing ones (at least on devices that I had opportunity to test the app).

However, this is not the case with iPadOS, and I believe Apple added a UI enabling users to open new windows around time when 15.3 came out. Or perhaps it was there all along and I haven't noticed it.

We never intended the app to run multiple copies of a primary scene. I have explored a possibility to keep intents on iPhone only but I read that it's likely that split screen is coming to phones in the next major OS release. I believe it's a possibility for larger phones and so I'd rather not introduce a feature that'll have to remove in the coming months.

As of I now I don't have a solution that would enable us to keep the app in single window mode and handle intents inside of main bundle. As it was highlighted before, intents extension cannot modify VPN configuration and multi-window support is required to handle intents inside of main bundle. 😕

ImTheSquid commented 1 year ago

Is there any progress on this issue? I took a quick look and at least for iOS 16+ there is an AppIntent protocol that may work with very little configuration, although it needs to be gated behind iOS 16. If this looks promising I'd be happy to try to take a stab at implementing it.

andelink commented 1 year ago

Not to dismiss this request for dedicated Mullvad Shortcut Actions, because I still think they would be valuable, but in the meantime I have found that the native "Set VPN" action works well enough. For example, I use this shortcut with only a single "Toggle WireGuard VPN" action in it. I've configured my double/triple-tap on the back of my phone to run it (and now the action button on iPhone 15).

buggmagnet commented 1 month ago

Hello,

We will discuss this with the team again. I'll leave this ticket open for the time being to gather more feedback / discussions. Thanks for your support !