diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake
index a498a5b1..ab820c71 100644
--- a/client/cmake/ios.cmake
+++ b/client/cmake/ios.cmake
@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
+ ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
)
diff --git a/client/ios/app/Info.plist.in b/client/ios/app/Info.plist.in
index 45b08cc9..6165daf3 100644
--- a/client/ios/app/Info.plist.in
+++ b/client/ios/app/Info.plist.in
@@ -32,17 +32,41 @@
UILaunchStoryboardName
AmneziaVPNLaunchScreen
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ Default Configuration
+ UISceneDelegateClassName
+ QIOSWindowSceneDelegate
+
+
+
+
UIRequiredDeviceCapabilities
UIRequiresFullScreen
-
+
UISupportedInterfaceOrientations
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationPortrait
UISupportedInterfaceOrientations~ipad
-
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
UIUserInterfaceStyle
Light
com.wireguard.ios.app_group_id
diff --git a/client/platforms/ios/AmneziaSceneDelegateHooks.mm b/client/platforms/ios/AmneziaSceneDelegateHooks.mm
new file mode 100644
index 00000000..60cbbe0f
--- /dev/null
+++ b/client/platforms/ios/AmneziaSceneDelegateHooks.mm
@@ -0,0 +1,82 @@
+#import
+#import
+#include
+
+#include
+#include
+#include
+
+#include "ios_controller.h"
+
+using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet *);
+
+static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr;
+
+static void amnezia_handleURL(NSURL *url)
+{
+ if (!url || !url.isFileURL) {
+ return;
+ }
+
+ QString filePath(url.path.UTF8String);
+ if (filePath.isEmpty()) {
+ return;
+ }
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ if (filePath.contains("backup")) {
+ IosController::Instance()->importBackupFromOutside(filePath);
+ return;
+ }
+
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ return;
+ }
+
+ const QByteArray data = file.readAll();
+ IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data));
+ });
+}
+
+static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet *contexts)
+{
+ if (g_originalSceneOpenURLContexts) {
+ g_originalSceneOpenURLContexts(self, _cmd, scene, contexts);
+ }
+
+ if (!contexts || contexts.count == 0) {
+ return;
+ }
+
+ if (@available(iOS 13.0, *)) {
+ for (UIOpenURLContext *context in contexts) {
+ amnezia_handleURL(context.URL);
+ }
+ }
+}
+
+@interface AmneziaSceneDelegateHooks : NSObject
+@end
+
+@implementation AmneziaSceneDelegateHooks
+
++ (void)load
+{
+ Class cls = objc_getClass("QIOSWindowSceneDelegate");
+ if (!cls) {
+ return;
+ }
+
+ SEL selector = @selector(scene:openURLContexts:);
+ Method method = class_getInstanceMethod(cls, selector);
+ if (method) {
+ g_originalSceneOpenURLContexts = reinterpret_cast(method_getImplementation(method));
+ method_setImplementation(method, reinterpret_cast(amnezia_scene_openURLContexts));
+ } else {
+ const char *types = "v@:@@";
+ class_addMethod(cls, selector, reinterpret_cast(amnezia_scene_openURLContexts), types);
+ }
+}
+
+@end
diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm
index b57f8d1d..64da50ea 100644
--- a/client/platforms/ios/ios_controller.mm
+++ b/client/platforms/ios/ios_controller.mm
@@ -29,12 +29,46 @@ const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
#if !MACOS_NE
static UIViewController* getViewController() {
- NSArray *windows = [[UIApplication sharedApplication]windows];
- for (UIWindow *window in windows) {
- if (window.isKeyWindow) {
+ UIApplication *application = [UIApplication sharedApplication];
+
+ if (@available(iOS 13.0, *)) {
+ for (UIScene *scene in application.connectedScenes) {
+ if (scene.activationState != UISceneActivationStateForegroundActive) {
+ continue;
+ }
+
+ if (![scene isKindOfClass:[UIWindowScene class]]) {
+ continue;
+ }
+
+ UIWindowScene *windowScene = (UIWindowScene *)scene;
+
+ for (UIWindow *window in windowScene.windows) {
+ if (window.isKeyWindow && window.rootViewController) {
+ return window.rootViewController;
+ }
+ }
+
+ for (UIWindow *window in windowScene.windows) {
+ if (!window.isHidden && window.rootViewController) {
+ return window.rootViewController;
+ }
+ }
+ }
+ }
+
+ for (UIWindow *window in application.windows) {
+ if (window.isKeyWindow && window.rootViewController) {
return window.rootViewController;
}
}
+
+ for (UIWindow *window in application.windows) {
+ if (window.rootViewController) {
+ return window.rootViewController;
+ }
+ }
+
return nil;
}
#endif