Procházet zdrojové kódy

[Apple embedded] Adopt SwiftUI lifecycle for Apple embedded platforms

- Introduces a SCons builder for Swift files
- Increases the minimum deployment targets to iOS 14.0, and visionOS 26.0.
- Replaces manually UIWindow management by a SwiftUI instantiated app.
Ricardo Sanchez-Saez před 1 měsícem
rodič
revize
038f5934e7
29 změnil soubory, kde provedl 346 přidání a 196 odebrání
  1. 17 1
      drivers/apple_embedded/SCsub
  2. 79 0
      drivers/apple_embedded/app.swift
  3. 1 2
      drivers/apple_embedded/app_delegate_service.h
  4. 15 23
      drivers/apple_embedded/app_delegate_service.mm
  5. 1 1
      drivers/apple_embedded/apple_embedded.mm
  6. 36 0
      drivers/apple_embedded/bridging_header_apple_embedded.h
  7. 4 5
      drivers/apple_embedded/display_server_apple_embedded.mm
  8. 6 0
      drivers/apple_embedded/godot_app_delegate.mm
  9. 1 1
      drivers/apple_embedded/godot_keyboard_input_view.h
  10. 2 2
      drivers/apple_embedded/godot_keyboard_input_view.mm
  11. 2 2
      drivers/apple_embedded/godot_view_apple_embedded.h
  12. 21 36
      drivers/apple_embedded/godot_view_apple_embedded.mm
  13. 1 1
      drivers/apple_embedded/godot_view_controller.h
  14. 13 3
      drivers/apple_embedded/godot_view_controller.mm
  15. 1 1
      drivers/apple_embedded/os_apple_embedded.mm
  16. 0 34
      editor/export/editor_export_platform_apple_embedded.cpp
  17. 0 1
      editor/export/editor_export_platform_apple_embedded.h
  18. 0 1
      editor/export/plugin_config_apple_embedded.cpp
  19. 0 3
      editor/export/plugin_config_apple_embedded.h
  20. 7 6
      methods.py
  21. 13 7
      misc/dist/apple_embedded_xcode/godot_apple_embedded.xcodeproj/project.pbxproj
  22. 16 15
      platform/ios/detect.py
  23. 8 1
      platform/ios/godot_view_ios.mm
  24. 1 15
      platform/ios/main_ios.mm
  25. 15 14
      platform/visionos/detect.py
  26. 1 1
      platform/visionos/export/export_plugin.h
  27. 3 1
      platform/visionos/godot_view_visionos.mm
  28. 0 19
      platform/visionos/main_visionos.mm
  29. 82 0
      platform_methods.py

+ 17 - 1
drivers/apple_embedded/SCsub

@@ -1,6 +1,10 @@
 #!/usr/bin/env python
 from misc.utility.scons_hints import *
 
+from SCons.Script import Glob
+
+from platform_methods import setup_swift_builder
+
 Import("env")
 
 env_apple_embedded = env.Clone()
@@ -8,9 +12,21 @@ env_apple_embedded = env.Clone()
 # Enable module support
 env_apple_embedded.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
 
+# Configure Swift builder
+apple_platform = env["APPLE_PLATFORM"]
+sdk_path = env["APPLE_SDK_PATH"]
+current_path = Dir(".").abspath
+bridging_header_filename = "bridging_header_apple_embedded.h"
+swift_files = Glob("*.swift")
+swift_file_names = list(map(lambda f: f.name, swift_files))
+setup_swift_builder(
+    env_apple_embedded, apple_platform, sdk_path, current_path, bridging_header_filename, swift_file_names
+)
+
 # Use bundled Vulkan headers
 vulkan_dir = "#thirdparty/vulkan"
 env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])
 
 # Driver source files
-env_apple_embedded.add_source_files(env.drivers_sources, "*.mm")
+env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.mm")
+env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.swift")

+ 79 - 0
drivers/apple_embedded/app.swift

@@ -0,0 +1,79 @@
+/**************************************************************************/
+/*  app.swift                                                             */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+import SwiftUI
+import UIKit
+
+struct GodotSwiftUIViewController: UIViewControllerRepresentable {
+
+	func makeUIViewController(context: Context) -> GDTViewController {
+		let viewController = GDTViewController()
+		GDTAppDelegateService.viewController = viewController
+		return viewController
+	}
+
+	func updateUIViewController(_ uiViewController: GDTViewController, context: Context) {
+		// NOOP
+	}
+
+}
+
+@main
+struct SwiftUIApp: App {
+	@UIApplicationDelegateAdaptor(GDTApplicationDelegate.self) var appDelegate
+	@Environment(\.scenePhase) private var scenePhase
+
+	var body: some Scene {
+		WindowGroup {
+			GodotSwiftUIViewController()
+				.ignoresSafeArea()
+				// UIViewControllerRepresentable does not call viewWillDisappear() nor viewDidDisappear() when
+				// backgrounding the app, or closing the app's main window, update the renderer here.
+				.onChange(of: scenePhase) { phase in
+					// For some reason UIViewControllerRepresentable is not calling viewWillDisappear()
+					// nor viewDidDisappear when closing the app's main window, call it here so we
+					// stop the renderer.
+					switch phase {
+					case .active:
+						print("GodotSwiftUIViewController scene active")
+						GDTAppDelegateService.viewController?.godotView.startRendering()
+					case .inactive:
+						print("GodotSwiftUIViewController scene inactive")
+						GDTAppDelegateService.viewController?.godotView.stopRendering()
+					case .background:
+						print("GodotSwiftUIViewController scene backgrounded")
+						GDTAppDelegateService.viewController?.godotView.stopRendering()
+					@unknown default:
+						print("unknown default")
+					}
+				}
+		}
+	}
+}

+ 1 - 2
drivers/apple_embedded/app_delegate_service.h

@@ -36,7 +36,6 @@
 
 @interface GDTAppDelegateService : NSObject <UIApplicationDelegate>
 
-@property(strong, nonatomic) UIWindow *window;
-@property(strong, class, readonly, nonatomic) GDTViewController *viewController;
+@property(strong, class, nonatomic) GDTViewController *viewController;
 
 @end

+ 15 - 23
drivers/apple_embedded/app_delegate_service.mm

@@ -31,8 +31,8 @@
 #import "app_delegate_service.h"
 
 #import "godot_view_apple_embedded.h"
+#import "godot_view_controller.h"
 #import "os_apple_embedded.h"
-#import "view_controller.h"
 
 #include "core/config/project_settings.h"
 #import "drivers/coreaudio/audio_driver_coreaudio.h"
@@ -41,10 +41,8 @@
 #import <AVFoundation/AVFoundation.h>
 #import <AudioToolbox/AudioServices.h>
 
-#define kRenderingFrequency 60
-
-extern int gargc;
-extern char **gargv;
+int gargc;
+char **gargv;
 
 extern int apple_embedded_main(int, char **);
 extern void apple_embedded_finish();
@@ -66,18 +64,23 @@ static GDTViewController *mainViewController = nil;
 	return mainViewController;
 }
 
++ (void)setViewController:(GDTViewController *)viewController {
+	mainViewController = viewController;
+}
+
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 	// TODO: might be required to make an early return, so app wouldn't crash because of timeout.
 	// TODO: logo screen is not displayed while shaders are compiling
 	// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
 
-#if !defined(VISIONOS_ENABLED)
-	// Create a full-screen window
-	CGRect windowBounds = [[UIScreen mainScreen] bounds];
-	self.window = [[UIWindow alloc] initWithFrame:windowBounds];
-#else
-	self.window = [[UIWindow alloc] init];
-#endif
+	// Fetch the command-line arguments from NSProcessInfo
+	NSArray *arguments = [[NSProcessInfo processInfo] arguments];
+	gargc = (int)[arguments count];
+	gargv = (char **)malloc(sizeof(char *) * gargc);
+	for (int i = 0; i < gargc; i++) {
+		NSString *arg = arguments[i];
+		gargv[i] = strdup([arg UTF8String]);
+	}
 
 	int err = apple_embedded_main(gargc, gargv);
 
@@ -87,23 +90,12 @@ static GDTViewController *mainViewController = nil;
 		return NO;
 	}
 
-	GDTViewController *viewController = [[GDTViewController alloc] init];
-	viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
-	viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
-
-	self.window.rootViewController = viewController;
-
-	// Show the window
-	[self.window makeKeyAndVisible];
-
 	[[NSNotificationCenter defaultCenter]
 			addObserver:self
 			   selector:@selector(onAudioInterruption:)
 				   name:AVAudioSessionInterruptionNotification
 				 object:[AVAudioSession sharedInstance]];
 
-	mainViewController = viewController;
-
 	int sessionCategorySetting = GLOBAL_GET("audio/general/ios/session_category");
 
 	// Initialize with default Ambient category.

+ 1 - 1
drivers/apple_embedded/apple_embedded.mm

@@ -31,7 +31,7 @@
 #import "apple_embedded.h"
 
 #import "app_delegate_service.h"
-#import "view_controller.h"
+#import "godot_view_controller.h"
 
 #import <CoreHaptics/CoreHaptics.h>
 #import <UIKit/UIKit.h>

+ 36 - 0
drivers/apple_embedded/bridging_header_apple_embedded.h

@@ -0,0 +1,36 @@
+/**************************************************************************/
+/*  bridging_header_apple_embedded.h                                      */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#import "app_delegate_service.h"
+#import "godot_app_delegate.h"
+#import "godot_view_apple_embedded.h"
+#import "godot_view_controller.h"

+ 4 - 5
drivers/apple_embedded/display_server_apple_embedded.mm

@@ -32,12 +32,12 @@
 
 #import "app_delegate_service.h"
 #import "apple_embedded.h"
+#import "godot_keyboard_input_view.h"
 #import "godot_view_apple_embedded.h"
+#import "godot_view_controller.h"
 #import "key_mapping_apple_embedded.h"
-#import "keyboard_input_view.h"
 #import "os_apple_embedded.h"
 #import "tts_apple_embedded.h"
-#import "view_controller.h"
 
 #include "core/config/project_settings.h"
 #include "core/io/file_access_pack.h"
@@ -612,9 +612,8 @@ void DisplayServerAppleEmbedded::window_set_size(const Size2i p_size, WindowID p
 }
 
 Size2i DisplayServerAppleEmbedded::window_get_size(WindowID p_window) const {
-	id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
-	CGRect windowBounds = appDelegate.window.bounds;
-	return Size2i(windowBounds.size.width, windowBounds.size.height) * screen_get_max_scale();
+	CGRect viewBounds = GDTAppDelegateService.viewController.view.bounds;
+	return Size2i(viewBounds.size.width, viewBounds.size.height) * screen_get_max_scale();
 }
 
 Size2i DisplayServerAppleEmbedded::window_get_size_with_decorations(WindowID p_window) const {

+ 6 - 0
drivers/apple_embedded/godot_app_delegate.mm

@@ -31,6 +31,7 @@
 #import "godot_app_delegate.h"
 
 #import "app_delegate_service.h"
+#include "core/typedefs.h"
 
 @implementation GDTApplicationDelegate
 
@@ -120,6 +121,9 @@ static NSMutableArray<GDTAppDelegateServiceProtocol *> *services = nil;
 
 // MARK: Life-Cycle
 
+// UIApplication lifecycle has become deprecated in favor of UIScene lifecycle
+GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations")
+
 - (void)applicationDidBecomeActive:(UIApplication *)application {
 	for (GDTAppDelegateServiceProtocol *service in services) {
 		if (![service respondsToSelector:_cmd]) {
@@ -461,3 +465,5 @@ static NSMutableArray<GDTAppDelegateServiceProtocol *> *services = nil;
 */
 
 @end
+
+GODOT_CLANG_WARNING_POP

+ 1 - 1
drivers/apple_embedded/keyboard_input_view.h → drivers/apple_embedded/godot_keyboard_input_view.h

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  keyboard_input_view.h                                                 */
+/*  godot_keyboard_input_view.h                                           */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */

+ 2 - 2
drivers/apple_embedded/keyboard_input_view.mm → drivers/apple_embedded/godot_keyboard_input_view.mm

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  keyboard_input_view.mm                                                */
+/*  godot_keyboard_input_view.mm                                          */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -28,7 +28,7 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-#import "keyboard_input_view.h"
+#import "godot_keyboard_input_view.h"
 
 #import "display_server_apple_embedded.h"
 #import "os_apple_embedded.h"

+ 2 - 2
drivers/apple_embedded/godot_view_apple_embedded.h

@@ -55,7 +55,7 @@ class String;
 @property(strong, readonly, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
 @property(assign, readonly, nonatomic) BOOL canRender;
 
-@property(assign, nonatomic) NSTimeInterval renderingInterval;
+@property(assign, nonatomic) float preferredFrameRate;
 
 // Can be extended by subclasses
 - (void)godot_commonInit;
@@ -63,8 +63,8 @@ class String;
 // Implemented in subclasses
 - (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
 
-- (void)stopRendering;
 - (void)startRendering;
+- (void)stopRendering;
 
 @end
 

+ 21 - 36
drivers/apple_embedded/godot_view_apple_embedded.mm

@@ -60,6 +60,8 @@ static const float earth_gravity = 9.80665;
 
 @property(strong, nonatomic) CMMotionManager *motionManager;
 
+@property(assign, nonatomic) BOOL delegateDidFinishSetUp;
+
 @end
 
 @implementation GDTView
@@ -117,6 +119,9 @@ static const float earth_gravity = 9.80665;
 }
 
 - (void)godot_commonInit {
+	self.preferredFrameRate = 60;
+	self.useCADisplayLink = bool(GLOBAL_DEF("display.AppleEmbedded/use_cadisplaylink", true)) ? YES : NO;
+
 #if !defined(VISIONOS_ENABLED)
 	self.contentScaleFactor = [UIScreen mainScreen].scale;
 #endif
@@ -197,17 +202,12 @@ static const float earth_gravity = 9.80665;
 
 	if (self.useCADisplayLink) {
 		self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
-
-		if (GLOBAL_GET("display/window/ios/allow_high_refresh_rate")) {
-			self.displayLink.preferredFramesPerSecond = 120;
-		} else {
-			self.displayLink.preferredFramesPerSecond = 60;
-		}
+		self.displayLink.preferredFramesPerSecond = self.preferredFrameRate;
 
 		// Setup DisplayLink in main thread
 		[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
 	} else {
-		self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60) target:self selector:@selector(drawView) userInfo:nil repeats:YES];
+		self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / self.preferredFrameRate) target:self selector:@selector(drawView) userInfo:nil repeats:YES];
 	}
 }
 
@@ -240,10 +240,10 @@ static const float earth_gravity = 9.80665;
 		return;
 	}
 
-	if (self.delegate) {
-		BOOL delegateFinishedSetup = [self.delegate godotViewFinishedSetup:self];
-
-		if (!delegateFinishedSetup) {
+	if (self.delegate && !self.delegateDidFinishSetUp) {
+		[self layoutRenderingLayer]; // Trigger DisplayServerVisionOS::resize_window after Main::start()
+		self.delegateDidFinishSetUp = [self.delegate godotViewFinishedSetup:self];
+		if (!_delegateDidFinishSetUp) {
 			return;
 		}
 	}
@@ -262,8 +262,8 @@ static const float earth_gravity = 9.80665;
 	}
 }
 
-- (void)setRenderingInterval:(NSTimeInterval)renderingInterval {
-	_renderingInterval = renderingInterval;
+- (void)setPreferredFrameRate:(float)preferredFrameRate {
+	_preferredFrameRate = preferredFrameRate;
 
 	if (self.canRender) {
 		[self stopRendering];
@@ -272,6 +272,11 @@ static const float earth_gravity = 9.80665;
 }
 
 - (void)layoutSubviews {
+	[super layoutSubviews];
+	[self layoutRenderingLayer];
+}
+
+- (void)layoutRenderingLayer {
 	if (self.renderingLayer) {
 		self.renderingLayer.frame = self.bounds;
 		[self.renderingLayer layoutDisplayLayer];
@@ -280,8 +285,6 @@ static const float earth_gravity = 9.80665;
 			DisplayServerAppleEmbedded::get_singleton()->resize_window(self.bounds.size);
 		}
 	}
-
-	[super layoutSubviews];
 }
 
 // MARK: - Input
@@ -415,28 +418,10 @@ static const float earth_gravity = 9.80665;
 	// our orientation which is not a good thing when you're trying to get
 	// your user to move the screen in all directions and want consistent
 	// output
-
-	///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
-	/// is a bit of a hack. Godot obviously knows the orientation so maybe
-	/// we
-	// can use that instead? (note that left and right seem swapped)
-
-	UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
-
-#if !defined(VISIONOS_ENABLED)
-#if __IPHONE_OS_VERSION_MAX_ALLOWED < 140000
-	interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
-#else
-	if (@available(iOS 13, *)) {
-		interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
-#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
-	} else {
-		interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
-#endif
-	}
-#endif
+#if defined(VISIONOS_ENABLED)
+	UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.effectiveGeometry.interfaceOrientation;
 #else
-	interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+	UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
 #endif
 
 	switch (interfaceOrientation) {

+ 1 - 1
drivers/apple_embedded/view_controller.h → drivers/apple_embedded/godot_view_controller.h

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  view_controller.h                                                     */
+/*  godot_view_controller.h                                               */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */

+ 13 - 3
drivers/apple_embedded/view_controller.mm → drivers/apple_embedded/godot_view_controller.mm

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  view_controller.mm                                                    */
+/*  godot_view_controller.mm                                              */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -28,13 +28,13 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-#import "view_controller.h"
+#import "godot_view_controller.h"
 
 #import "display_server_apple_embedded.h"
+#import "godot_keyboard_input_view.h"
 #import "godot_view_apple_embedded.h"
 #import "godot_view_renderer.h"
 #import "key_mapping_apple_embedded.h"
-#import "keyboard_input_view.h"
 #import "os_apple_embedded.h"
 
 #include "core/config/project_settings.h"
@@ -168,6 +168,16 @@
 	[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
 }
 
+- (void)viewDidAppear:(BOOL)animated {
+	[super viewDidAppear:animated];
+	[self.godotView startRendering];
+}
+
+- (void)viewDidDisappear:(BOOL)animated {
+	[self.godotView stopRendering];
+	[super viewDidDisappear:animated];
+}
+
 - (void)observeKeyboard {
 	print_verbose("Setting up keyboard input view.");
 	self.keyboardView = [GDTKeyboardInputView new];

+ 1 - 1
drivers/apple_embedded/os_apple_embedded.mm

@@ -35,7 +35,7 @@
 #import "app_delegate_service.h"
 #import "display_server_apple_embedded.h"
 #import "godot_view_apple_embedded.h"
-#import "view_controller.h"
+#import "godot_view_controller.h"
 
 #include "core/config/project_settings.h"
 #include "core/io/dir_access.h"

+ 0 - 34
editor/export/editor_export_platform_apple_embedded.cpp

@@ -638,35 +638,6 @@ String EditorExportPlatformAppleEmbedded::_process_config_file_line(const Ref<Ed
 			}
 		}
 		strnew += p_line.replace("$pbx_locale_build_reference", locale_files);
-	} else if (p_line.contains("$swift_runtime_migration")) {
-		String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;";
-		strnew += p_line.replace("$swift_runtime_migration", value) + "\n";
-	} else if (p_line.contains("$swift_runtime_build_settings")) {
-		String value = !p_config.use_swift_runtime ? "" : R"(
-				 CLANG_ENABLE_MODULES = YES;
-				 SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
-				 SWIFT_VERSION = 5.0;
-				 )";
-		value = value.replace("$binary", p_config.binary_name);
-		strnew += p_line.replace("$swift_runtime_build_settings", value) + "\n";
-	} else if (p_line.contains("$swift_runtime_fileref")) {
-		String value = !p_config.use_swift_runtime ? "" : R"(
-				 90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; };
-				 90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; };
-				 )";
-		strnew += p_line.replace("$swift_runtime_fileref", value) + "\n";
-	} else if (p_line.contains("$swift_runtime_binary_files")) {
-		String value = !p_config.use_swift_runtime ? "" : R"(
-				 90B4C2AA2680BC560039117A /* dummy.h */,
-				 90B4C2B52680C7E90039117A /* dummy.swift */,
-				 )";
-		strnew += p_line.replace("$swift_runtime_binary_files", value) + "\n";
-	} else if (p_line.contains("$swift_runtime_buildfile")) {
-		String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };";
-		strnew += p_line.replace("$swift_runtime_buildfile", value) + "\n";
-	} else if (p_line.contains("$swift_runtime_build_phase")) {
-		String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
-		strnew += p_line.replace("$swift_runtime_build_phase", value) + "\n";
 	} else if (p_line.contains("$priv_collection")) {
 		bool section_opened = false;
 		for (uint64_t j = 0; j < std::size(data_collect_type_info); ++j) {
@@ -1566,10 +1537,6 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re
 
 		plugin_initialization_cpp_code += "\t" + initialization_method;
 		plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
-
-		if (plugin.use_swift_runtime) {
-			p_config_data.use_swift_runtime = true;
-		}
 	}
 
 	// Updating `Info.plist`
@@ -1819,7 +1786,6 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
 		"",
 		"",
 		Vector<String>(),
-		false
 	};
 
 	config_data.plist_content += p_preset->get("application/additional_plist_content").operator String() + "\n";

+ 0 - 1
editor/export/editor_export_platform_apple_embedded.h

@@ -142,7 +142,6 @@ protected:
 		String modules_buildphase;
 		String modules_buildgrp;
 		Vector<String> capabilities;
-		bool use_swift_runtime;
 	};
 
 	struct CodeSigningDetails {

+ 0 - 1
editor/export/plugin_config_apple_embedded.cpp

@@ -184,7 +184,6 @@ PluginConfigAppleEmbedded PluginConfigAppleEmbedded::load_plugin_config(Ref<Conf
 	String config_base_dir = path.get_base_dir();
 
 	plugin_config.name = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_NAME_KEY, String());
-	plugin_config.use_swift_runtime = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_USE_SWIFT_KEY, false);
 	plugin_config.initialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_INITIALIZE_KEY, String());
 	plugin_config.deinitialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_DEINITIALIZE_KEY, String());
 

+ 0 - 3
editor/export/plugin_config_apple_embedded.h

@@ -37,7 +37,6 @@
  The `config` section and fields are required and defined as follow:
 - **name**: name of the plugin
 - **binary**: path to static `.a` library
-- **use_swift_runtime**: optional boolean field used to determine if Swift runtime is used
 
 The `dependencies` and fields are optional.
 - **linked**: dependencies that should only be linked.
@@ -56,7 +55,6 @@ struct PluginConfigAppleEmbedded {
 	inline static const char *CONFIG_SECTION = "config";
 	inline static const char *CONFIG_NAME_KEY = "name";
 	inline static const char *CONFIG_BINARY_KEY = "binary";
-	inline static const char *CONFIG_USE_SWIFT_KEY = "use_swift_runtime";
 	inline static const char *CONFIG_INITIALIZE_KEY = "initialization";
 	inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization";
 
@@ -93,7 +91,6 @@ struct PluginConfigAppleEmbedded {
 	// Required config section
 	String name;
 	String binary;
-	bool use_swift_runtime;
 	String initialization_method;
 	String deinitialization_method;
 

+ 7 - 6
methods.py

@@ -42,8 +42,8 @@ def add_source_files_orig(self, sources, files, allow_gen=False):
             files = [f for f in files if not str(f).endswith(".gen.cpp")]
 
     # Add each path as compiled Object following environment (self) configuration
-    for path in files:
-        obj = self.Object(path)
+    for file in files:
+        obj = self.Object(file)
         if obj in sources:
             print_warning('Object "{}" already included in environment sources.'.format(obj))
             continue
@@ -437,6 +437,7 @@ def no_verbose(env):
 
     env["CXXCOMSTR"] = compile_source_message
     env["CCCOMSTR"] = compile_source_message
+    env["SWIFTCOMSTR"] = compile_source_message
     env["SHCCCOMSTR"] = compile_shared_source_message
     env["SHCXXCOMSTR"] = compile_shared_source_message
     env["ARCOMSTR"] = link_library_message
@@ -630,19 +631,19 @@ def detect_darwin_sdk_path(platform, env):
 
     elif platform == "ios":
         sdk_name = "iphoneos"
-        var_name = "IOS_SDK_PATH"
+        var_name = "APPLE_SDK_PATH"
 
     elif platform == "iossimulator":
         sdk_name = "iphonesimulator"
-        var_name = "IOS_SDK_PATH"
+        var_name = "APPLE_SDK_PATH"
 
     elif platform == "visionos":
         sdk_name = "xros"
-        var_name = "VISIONOS_SDK_PATH"
+        var_name = "APPLE_SDK_PATH"
 
     elif platform == "visionossimulator":
         sdk_name = "xrsimulator"
-        var_name = "VISIONOS_SDK_PATH"
+        var_name = "APPLE_SDK_PATH"
 
     else:
         raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")

+ 13 - 7
misc/dist/apple_embedded_xcode/godot_apple_embedded.xcodeproj/project.pbxproj

@@ -10,7 +10,7 @@
 		1F1575721F582BE20003B888 /* dylibs in Resources */ = {isa = PBXBuildFile; fileRef = 1F1575711F582BE20003B888 /* dylibs */; };
 		DEADBEEF2F582BE20003B888 /* $binary.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEADBEEF1F582BE20003B888 /* $binary.xcframework */; };
 		$modules_buildfile
-		$swift_runtime_buildfile
+		90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };
 		1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */; };
 		D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
         $moltenvk_buildfile
@@ -38,7 +38,8 @@
 		1F1575711F582BE20003B888 /* dylibs */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dylibs; path = "$binary/dylibs"; sourceTree = "<group>"; };
 		DEADBEEF1F582BE20003B888 /* $binary.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = godot; path = "$binary.xcframework"; sourceTree = "<group>"; };
 		$modules_fileref
-		$swift_runtime_fileref
+		90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; };
+		90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; };
 		1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "$binary.entitlements"; sourceTree = "<group>"; };
 		1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
         $moltenvk_fileref
@@ -109,7 +110,8 @@
 				D07CD44D1C5D589C00B7FB28 /* Images.xcassets */,
 				D0BCFE4218AEBDA2004A7AAE /* Supporting Files */,
 				1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */,
-				$swift_runtime_binary_files
+				90B4C2AA2680BC560039117A /* dummy.h */,
+				90B4C2B52680C7E90039117A /* dummy.swift */,
 			);
 			path = "$binary";
 			sourceTree = "<group>";
@@ -155,7 +157,7 @@
 				TargetAttributes = {
 					D0BCFE3318AEBDA2004A7AAE = {
 						DevelopmentTeam = $team_id;
-						$swift_runtime_migration
+						LastSwiftMigration = 1250;
 						ProvisioningStyle = Automatic;
 						SystemCapabilities = {
 						};
@@ -202,7 +204,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */,
-				$swift_runtime_build_phase
+				90B4C2B62680C7E90039117A /* dummy.swift */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -340,7 +342,9 @@
 				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 				VALID_ARCHS = "$valid_archs";
 				WRAPPER_EXTENSION = app;
-				$swift_runtime_build_settings
+				CLANG_ENABLE_MODULES = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
+				SWIFT_VERSION = 5.0;
 			};
 			name = Debug;
 		};
@@ -375,7 +379,9 @@
 				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 				VALID_ARCHS = "$valid_archs";
 				WRAPPER_EXTENSION = app;
-				$swift_runtime_build_settings
+				CLANG_ENABLE_MODULES = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
+				SWIFT_VERSION = 5.0;
 			};
 			name = Release;
 		};

+ 16 - 15
platform/ios/detect.py

@@ -27,7 +27,7 @@ def get_opts():
         ("vulkan_sdk_path", "Path to the Vulkan SDK", ""),
         # APPLE_TOOLCHAIN_PATH Example: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
         (("APPLE_TOOLCHAIN_PATH", "IOS_TOOLCHAIN_PATH"), "Path to the Apple toolchain", ""),
-        ("IOS_SDK_PATH", "Path to the iOS SDK", ""),
+        (("APPLE_SDK_PATH", "IOS_SDK_PATH"), "Path to the iOS SDK", ""),
         (("apple_target_triple", "ios_triple"), "Triple for the corresponding target Apple platform toolchain", ""),
         BoolVariable(("simulator", "ios_simulator"), "Build for Simulator", False),
         BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
@@ -101,15 +101,16 @@ def configure(env: "SConsEnvironment"):
     ## Compile flags
 
     if env["simulator"]:
-        detect_darwin_sdk_path("iossimulator", env)
-        env.Append(ASFLAGS=["-mios-simulator-version-min=12.0"])
-        env.Append(CCFLAGS=["-mios-simulator-version-min=12.0"])
+        env["APPLE_PLATFORM"] = "iossimulator"
+        env.Append(ASFLAGS=["-mios-simulator-version-min=14.0"])
+        env.Append(CCFLAGS=["-mios-simulator-version-min=14.0"])
         env.Append(CPPDEFINES=["IOS_SIMULATOR"])
         env.extra_suffix = ".simulator" + env.extra_suffix
     else:
-        detect_darwin_sdk_path("ios", env)
-        env.Append(ASFLAGS=["-miphoneos-version-min=12.0"])
-        env.Append(CCFLAGS=["-miphoneos-version-min=12.0"])
+        env["APPLE_PLATFORM"] = "ios"
+        env.Append(ASFLAGS=["-miphoneos-version-min=14.0"])
+        env.Append(CCFLAGS=["-miphoneos-version-min=14.0"])
+    detect_darwin_sdk_path(env["APPLE_PLATFORM"], env)
 
     if env["arch"] == "x86_64":
         if not env["simulator"]:
@@ -121,7 +122,7 @@ def configure(env: "SConsEnvironment"):
             CCFLAGS=(
                 "-fobjc-arc -arch x86_64"
                 " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks"
-                " -fasm-blocks -isysroot $IOS_SDK_PATH"
+                " -fasm-blocks -isysroot $APPLE_SDK_PATH"
             ).split()
         )
         env.Append(ASFLAGS=["-arch", "x86_64"])
@@ -131,7 +132,7 @@ def configure(env: "SConsEnvironment"):
                 "-fobjc-arc -arch arm64 -fmessage-length=0"
                 " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
                 " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies"
-                " -isysroot $IOS_SDK_PATH".split()
+                " -isysroot $APPLE_SDK_PATH".split()
             )
         )
         env.Append(ASFLAGS=["-arch", "arm64"])
@@ -141,8 +142,8 @@ def configure(env: "SConsEnvironment"):
 
     env.Prepend(
         CPPPATH=[
-            "$IOS_SDK_PATH/usr/include",
-            "$IOS_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
+            "$APPLE_SDK_PATH/usr/include",
+            "$APPLE_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
         ]
     )
 
@@ -157,9 +158,9 @@ def configure(env: "SConsEnvironment"):
         env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
         env.Prepend(
             CPPPATH=[
-                "$IOS_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
-                "$IOS_SDK_PATH/System/Library/Frameworks/MetalFX.framework/Headers",
-                "$IOS_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/MetalFX.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
             ]
         )
         env.Prepend(CPPEXTPATH=["#thirdparty/spirv-cross"])
@@ -176,6 +177,6 @@ def configure(env: "SConsEnvironment"):
         env.Append(CCFLAGS=["-Wno-module-import-in-extern-c"])
         env.Prepend(
             CPPPATH=[
-                "$IOS_SDK_PATH/System/Library/Frameworks/OpenGLES.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/OpenGLES.framework/Headers",
             ]
         )

+ 8 - 1
platform/ios/godot_view_ios.mm

@@ -32,6 +32,7 @@
 
 #import "display_layer_ios.h"
 
+#include "core/config/project_settings.h"
 #include "core/error/error_macros.h"
 
 @interface GDTViewIOS ()
@@ -83,5 +84,11 @@ GODOT_CLANG_WARNING_POP
 @end
 
 GDTView *GDTViewCreate() {
-	return [GDTViewIOS new];
+	GDTViewIOS *view = [GDTViewIOS new];
+	if (GLOBAL_GET("display/window/ios/allow_high_refresh_rate")) {
+		view.preferredFrameRate = 120;
+	} else {
+		view.preferredFrameRate = 60;
+	}
+	return view;
 }

+ 1 - 15
platform/ios/main_ios.mm

@@ -37,28 +37,14 @@
 #import <UIKit/UIKit.h>
 #include <cstdio>
 
-int gargc;
-char **gargv;
-
 static OS_IOS *os = nullptr;
 
-int main(int argc, char *argv[]) {
+int apple_embedded_main(int argc, char **argv) {
 #if defined(VULKAN_ENABLED)
 	//MoltenVK - enable full component swizzling support
 	setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
 #endif
 
-	gargc = argc;
-	gargv = argv;
-
-	@autoreleasepool {
-		NSString *className = NSStringFromClass([GDTApplicationDelegate class]);
-		UIApplicationMain(argc, argv, nil, className);
-	}
-	return 0;
-}
-
-int apple_embedded_main(int argc, char **argv) {
 	change_to_launch_dir(argv);
 
 	os = new OS_IOS();

+ 15 - 14
platform/visionos/detect.py

@@ -25,8 +25,8 @@ def get_opts():
 
     return [
         # APPLE_TOOLCHAIN_PATH Example: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
-        (("APPLE_TOOLCHAIN_PATH", "IOS_TOOLCHAIN_PATH"), "Path to the Apple toolchain", ""),
-        ("VISIONOS_SDK_PATH", "Path to the visionOS SDK", ""),
+        ("APPLE_TOOLCHAIN_PATH", "Path to the Apple toolchain", ""),
+        (("APPLE_SDK_PATH", "VISIONOS_SDK_PATH"), "Path to the visionOS SDK", ""),
         ("apple_target_triple", "Triple for corresponding target Apple platform toolchain", ""),
         BoolVariable("simulator", "Build for Simulator", False),
         BoolVariable("generate_bundle", "Generate an APP bundle after building visionOS/macOS binaries", False),
@@ -102,15 +102,16 @@ def configure(env: "SConsEnvironment"):
     ## Compile flags
 
     if env["simulator"]:
-        detect_darwin_sdk_path("visionossimulator", env)
-        env.Append(ASFLAGS=["-mtargetos=xros2.0-simulator"])
-        env.Append(CCFLAGS=["-mtargetos=xros2.0-simulator"])
+        env["APPLE_PLATFORM"] = "visionossimulator"
+        env.Append(ASFLAGS=["-mtargetos=xros26.0-simulator"])
+        env.Append(CCFLAGS=["-mtargetos=xros26.0-simulator"])
         env.Append(CPPDEFINES=["VISIONOS_SIMULATOR"])
         env.extra_suffix = ".simulator" + env.extra_suffix
     else:
-        detect_darwin_sdk_path("visionos", env)
-        env.Append(ASFLAGS=["-mtargetos=xros2.0"])
-        env.Append(CCFLAGS=["-mtargetos=xros2.0"])
+        env["APPLE_PLATFORM"] = "visionos"
+        env.Append(ASFLAGS=["-mtargetos=xros26.0"])
+        env.Append(CCFLAGS=["-mtargetos=xros26.0"])
+    detect_darwin_sdk_path(env["APPLE_PLATFORM"], env)
 
     if env["arch"] == "arm64":
         env.Append(
@@ -118,7 +119,7 @@ def configure(env: "SConsEnvironment"):
                 "-fobjc-arc -arch arm64 -fmessage-length=0"
                 " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
                 " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies"
-                " -isysroot $VISIONOS_SDK_PATH".split()
+                " -isysroot $APPLE_SDK_PATH".split()
             )
         )
         env.Append(ASFLAGS=["-arch", "arm64"])
@@ -128,8 +129,8 @@ def configure(env: "SConsEnvironment"):
 
     env.Prepend(
         CPPPATH=[
-            "$VISIONOS_SDK_PATH/usr/include",
-            "$VISIONOS_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
+            "$APPLE_SDK_PATH/usr/include",
+            "$APPLE_SDK_PATH/System/Library/Frameworks/AudioUnit.framework/Headers",
         ]
     )
 
@@ -148,9 +149,9 @@ def configure(env: "SConsEnvironment"):
         env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
         env.Prepend(
             CPPPATH=[
-                "$VISIONOS_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
-                "$VISIONOS_SDK_PATH/System/Library/Frameworks/MetalFX.framework/Headers",
-                "$VISIONOS_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/MetalFX.framework/Headers",
+                "$APPLE_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
             ]
         )
         env.Prepend(CPPPATH=["#thirdparty/spirv-cross"])

+ 1 - 1
platform/visionos/export/export_plugin.h

@@ -41,7 +41,7 @@ class EditorExportPlatformVisionOS : public EditorExportPlatformAppleEmbedded {
 	virtual String get_sdk_name() const override { return "xros"; }
 	virtual const Vector<String> get_device_types() const override { return device_types; }
 
-	virtual String get_minimum_deployment_target() const override { return "2.0"; }
+	virtual String get_minimum_deployment_target() const override { return "26.0"; }
 
 	virtual Vector<EditorExportPlatformAppleEmbedded::IconInfo> get_icon_infos() const override;
 

+ 3 - 1
platform/visionos/godot_view_visionos.mm

@@ -76,5 +76,7 @@ GODOT_CLANG_WARNING_POP
 @end
 
 GDTView *GDTViewCreate() {
-	return [GDTViewVisionOS new];
+	GDTViewVisionOS *view = [GDTViewVisionOS new];
+	view.preferredFrameRate = 90;
+	return view;
 }

+ 0 - 19
platform/visionos/main_visionos.mm

@@ -37,27 +37,8 @@
 #import <UIKit/UIKit.h>
 #include <cstdio>
 
-int gargc;
-char **gargv;
-
 static OS_VisionOS *os = nullptr;
 
-int main(int argc, char *argv[]) {
-#if defined(VULKAN_ENABLED)
-	//MoltenVK - enable full component swizzling support
-	setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
-#endif
-
-	gargc = argc;
-	gargv = argv;
-
-	@autoreleasepool {
-		NSString *className = NSStringFromClass([GDTApplicationDelegate class]);
-		UIApplicationMain(argc, argv, nil, className);
-	}
-	return 0;
-}
-
 int apple_embedded_main(int argc, char **argv) {
 	change_to_launch_dir(argv);
 

+ 82 - 0
platform_methods.py

@@ -243,3 +243,85 @@ def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, u
     zip_dir = env.Dir("#bin/" + (app_prefix + extra_suffix).replace(".", "_")).abspath
     shutil.make_archive(zip_dir, "zip", root_dir=app_dir)
     shutil.rmtree(app_dir)
+
+
+def setup_swift_builder(env, apple_platform, sdk_path, current_path, bridging_header_filename, all_swift_files):
+    from SCons.Script import Action, Builder
+
+    if apple_platform == "macos":
+        target_suffix = "macosx10.9"
+
+    elif apple_platform == "ios":
+        target_suffix = "ios14.0"  # iOS 14.0 needed for SwiftUI lifecycle
+
+    elif apple_platform == "iossimulator":
+        target_suffix = "ios14.0-simulator"  # iOS 14.0 needed for SwiftUI lifecycle
+
+    elif apple_platform == "visionos":
+        target_suffix = "xros26.0"
+
+    elif apple_platform == "visionossimulator":
+        target_suffix = "xros26.0-simulator"
+
+    else:
+        raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")
+
+    swiftc_target = env["arch"] + "-apple-" + target_suffix
+
+    env["ALL_SWIFT_FILES"] = all_swift_files
+    env["CURRENT_PATH"] = current_path
+    frontend_path = "$APPLE_TOOLCHAIN_PATH/usr/bin/swift-frontend"
+    bridging_header_path = current_path + "/" + bridging_header_filename
+    env["SWIFTC"] = frontend_path + " -frontend -c"  # Swift compiler
+    env["SWIFTCFLAGS"] = [
+        "-cxx-interoperability-mode=default",
+        "-emit-object",
+        "-target",
+        swiftc_target,
+        "-sdk",
+        sdk_path,
+        "-import-objc-header",
+        bridging_header_path,
+        "-swift-version",
+        "6",
+        "-parse-as-library",
+        "-module-name",
+        "godot_swift_module",
+        "-I./",  # Pass the current directory as the header root so bridging headers can include files from any point of the hierarchy
+    ]
+
+    if env["debug_symbols"]:
+        env.Append(SWIFTCFLAGS=["-g"])
+
+    if env["optimize"] in ["speed", "speed_trace"]:
+        env.Append(SWIFTCFLAGS=["-O"])
+
+    elif env["optimize"] == "size":
+        env.Append(SWIFTCFLAGS=["-Osize"])
+
+    elif env["optimize"] in ["debug", "none"]:
+        env.Append(SWIFTCFLAGS=["-Onone"])
+
+    def generate_swift_action(source, target, env, for_signature):
+        fullpath_swift_files = [env["CURRENT_PATH"] + "/" + file for file in env["ALL_SWIFT_FILES"]]
+        fullpath_swift_files.remove(source[0].abspath)
+
+        fullpath_swift_files_string = '"' + '" "'.join(fullpath_swift_files) + '"'
+        compile_command = "$SWIFTC " + fullpath_swift_files_string + " -primary-file $SOURCE -o $TARGET $SWIFTCFLAGS"
+
+        swift_comdstr = env.get("SWIFTCOMSTR")
+        if swift_comdstr is not None:
+            swift_action = Action(compile_command, cmdstr=swift_comdstr)
+        else:
+            swift_action = Action(compile_command)
+
+        return swift_action
+
+    # Define Builder for Swift files
+    swift_builder = Builder(
+        generator=generate_swift_action, suffix=env["OBJSUFFIX"], src_suffix=".swift", emitter=methods.redirect_emitter
+    )
+
+    env.Append(BUILDERS={"Swift": swift_builder})
+    env["BUILDERS"]["Library"].add_src_builder("Swift")
+    env["BUILDERS"]["Object"].add_action(".swift", Action(generate_swift_action, generator=1))