Bläddra i källkod

Merge pull request #42503 from naithar/feature/ios-cleanup-arc

[4.0] [iOS] Switch to ARC. Refactoring and cleanup.
Rémi Verschelde 5 år sedan
förälder
incheckning
0d07a935e9

+ 2 - 2
misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj

@@ -386,7 +386,7 @@
 				CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
 				DEVELOPMENT_TEAM = $team_id;
 				INFOPLIST_FILE = "$binary/$binary-Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -416,7 +416,7 @@
 				CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
 				DEVELOPMENT_TEAM = $team_id;
 				INFOPLIST_FILE = "$binary/$binary-Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",

+ 11 - 2
modules/arkit/arkit_interface.h

@@ -44,6 +44,15 @@
 // forward declaration for some needed objects
 class ARKitShader;
 
+#ifdef __OBJC__
+
+typedef ARAnchor GodotARAnchor;
+
+#else
+
+typedef void GodotARAnchor;
+#endif
+
 class ARKitInterface : public XRInterface {
 	GDCLASS(ARKitInterface, XRInterface);
 
@@ -115,8 +124,8 @@ public:
 	virtual void process() override;
 
 	// called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm)
-	void _add_or_update_anchor(void *p_anchor);
-	void _remove_anchor(void *p_anchor);
+	void _add_or_update_anchor(GodotARAnchor *p_anchor);
+	void _remove_anchor(GodotARAnchor *p_anchor);
 
 	ARKitInterface();
 	~ARKitInterface();

+ 5 - 7
modules/arkit/arkit_interface.mm

@@ -306,12 +306,10 @@ void ARKitInterface::uninitialize() {
 		remove_all_anchors();
 
 		if (@available(iOS 11.0, *)) {
-			[ar_session release];
-			ar_session = NULL;
+			ar_session = nil;
 		}
-		[ar_delegate release];
 
-		ar_delegate = NULL;
+		ar_delegate = nil;
 		initialized = false;
 		session_was_started = false;
 	}
@@ -687,7 +685,7 @@ void ARKitInterface::process() {
 	}
 }
 
-void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
+void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) {
 	// _THREAD_SAFE_METHOD_
 
 	if (@available(iOS 11.0, *)) {
@@ -749,7 +747,7 @@ void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
 	}
 }
 
-void ARKitInterface::_remove_anchor(void *p_anchor) {
+void ARKitInterface::_remove_anchor(GodotARAnchor *p_anchor) {
 	// _THREAD_SAFE_METHOD_
 
 	if (@available(iOS 11.0, *)) {
@@ -768,7 +766,7 @@ ARKitInterface::ARKitInterface() {
 	plane_detection_is_enabled = false;
 	light_estimation_is_enabled = false;
 	if (@available(iOS 11.0, *)) {
-		ar_session = NULL;
+		ar_session = nil;
 	}
 	z_near = 0.01;
 	z_far = 1000.0;

+ 6 - 18
modules/camera/camera_ios.mm

@@ -124,18 +124,12 @@
 	if (output) {
 		[self removeOutput:output];
 		[output setSampleBufferDelegate:nil queue:NULL];
-		[output release];
 		output = nil;
 	}
 
 	[self commitConfiguration];
 }
 
-- (void)dealloc {
-	// bye bye
-	[super dealloc];
-}
-
 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 	// This gets called every time our camera has a new image for us to process.
 	// May need to investigate in a way to throttle this if we get more images then we're rendering frames..
@@ -272,7 +266,6 @@ CameraFeedIOS::CameraFeedIOS() {
 
 void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
 	device = p_device;
-	[device retain];
 
 	// get some info
 	NSString *device_name = p_device.localizedName;
@@ -286,14 +279,12 @@ void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
 };
 
 CameraFeedIOS::~CameraFeedIOS() {
-	if (capture_session != NULL) {
-		[capture_session release];
-		capture_session = NULL;
+	if (capture_session) {
+		capture_session = nil;
 	};
 
-	if (device != NULL) {
-		[device release];
-		device = NULL;
+	if (device) {
+		device = nil;
 	};
 };
 
@@ -312,8 +303,7 @@ void CameraFeedIOS::deactivate_feed() {
 	// end camera capture if we have one
 	if (capture_session) {
 		[capture_session cleanup];
-		[capture_session release];
-		capture_session = NULL;
+		capture_session = nil;
 	};
 };
 
@@ -347,8 +337,6 @@ void CameraFeedIOS::deactivate_feed() {
 	// remove notifications
 	[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
 	[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
-
-	[super dealloc];
 }
 
 @end
@@ -453,5 +441,5 @@ CameraIOS::CameraIOS() {
 };
 
 CameraIOS::~CameraIOS() {
-	[device_notifications release];
+	device_notifications = nil;
 };

+ 1 - 2
modules/mono/mono_gd/support/ios_support.mm

@@ -131,8 +131,7 @@ GD_PINVOKE_EXPORT void *xamarin_timezone_get_data(const char *p_name, uint32_t *
 	NSTimeZone *tz = nil;
 	if (p_name) {
 		NSString *n = [[NSString alloc] initWithUTF8String:p_name];
-		tz = [[[NSTimeZone alloc] initWithName:n] autorelease];
-		[n release];
+		tz = [[NSTimeZone alloc] initWithName:n];
 	} else {
 		tz = [NSTimeZone localTimeZone];
 	}

+ 2 - 0
platform/iphone/SCsub

@@ -19,6 +19,8 @@ iphone_lib = [
     "display_layer.mm",
     "godot_view_renderer.mm",
     "godot_view_gesture_recognizer.mm",
+    "device_metrics.m",
+    "native_video_view.m",
 ]
 
 env_ios = env.Clone()

+ 1 - 2
platform/iphone/app_delegate.mm

@@ -62,7 +62,7 @@ static ViewController *mainViewController = nil;
 	CGRect windowBounds = [[UIScreen mainScreen] bounds];
 
 	// Create a full-screen window
-	self.window = [[[UIWindow alloc] initWithFrame:windowBounds] autorelease];
+	self.window = [[UIWindow alloc] initWithFrame:windowBounds];
 
 	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 	NSString *documentsDirectory = [paths objectAtIndex:0];
@@ -140,7 +140,6 @@ static ViewController *mainViewController = nil;
 
 - (void)dealloc {
 	self.window = nil;
-	[super dealloc];
 }
 
 @end

+ 2 - 2
platform/iphone/detect.py

@@ -129,7 +129,7 @@ def configure(env):
         detect_darwin_sdk_path("iphone", env)
         env.Append(
             CCFLAGS=(
-                "-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing"
+                "-fobjc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing"
                 " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
                 " -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb"
                 ' "-DIBOutlet=__attribute__((iboutlet))"'
@@ -141,7 +141,7 @@ def configure(env):
         detect_darwin_sdk_path("iphone", env)
         env.Append(
             CCFLAGS=(
-                "-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing"
+                "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing"
                 " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
                 " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=11.0"
                 " -isysroot $IPHONESDK".split()

+ 37 - 0
platform/iphone/device_metrics.h

@@ -0,0 +1,37 @@
+/*************************************************************************/
+/*  device_metrics.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 <Foundation/Foundation.h>
+
+@interface GodotDeviceMetrics : NSObject
+
+@property(nonatomic, class, readonly, strong) NSDictionary<NSArray *, NSNumber *> *dpiList;
+
+@end

+ 152 - 0
platform/iphone/device_metrics.m

@@ -0,0 +1,152 @@
+/*************************************************************************/
+/*  device_metrics.m                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 "device_metrics.h"
+
+@implementation GodotDeviceMetrics
+
++ (NSDictionary *)dpiList {
+	return @{
+		@[
+			@"iPad1,1",
+			@"iPad2,1",
+			@"iPad2,2",
+			@"iPad2,3",
+			@"iPad2,4",
+		] : @132,
+		@[
+			@"iPhone1,1",
+			@"iPhone1,2",
+			@"iPhone2,1",
+			@"iPad2,5",
+			@"iPad2,6",
+			@"iPad2,7",
+			@"iPod1,1",
+			@"iPod2,1",
+			@"iPod3,1",
+		] : @163,
+		@[
+			@"iPad3,1",
+			@"iPad3,2",
+			@"iPad3,3",
+			@"iPad3,4",
+			@"iPad3,5",
+			@"iPad3,6",
+			@"iPad4,1",
+			@"iPad4,2",
+			@"iPad4,3",
+			@"iPad5,3",
+			@"iPad5,4",
+			@"iPad6,3",
+			@"iPad6,4",
+			@"iPad6,7",
+			@"iPad6,8",
+			@"iPad6,11",
+			@"iPad6,12",
+			@"iPad7,1",
+			@"iPad7,2",
+			@"iPad7,3",
+			@"iPad7,4",
+			@"iPad7,5",
+			@"iPad7,6",
+			@"iPad7,11",
+			@"iPad7,12",
+			@"iPad8,1",
+			@"iPad8,2",
+			@"iPad8,3",
+			@"iPad8,4",
+			@"iPad8,5",
+			@"iPad8,6",
+			@"iPad8,7",
+			@"iPad8,8",
+			@"iPad8,9",
+			@"iPad8,10",
+			@"iPad8,11",
+			@"iPad8,12",
+			@"iPad11,3",
+			@"iPad11,4",
+		] : @264,
+		@[
+			@"iPhone3,1",
+			@"iPhone3,2",
+			@"iPhone3,3",
+			@"iPhone4,1",
+			@"iPhone5,1",
+			@"iPhone5,2",
+			@"iPhone5,3",
+			@"iPhone5,4",
+			@"iPhone6,1",
+			@"iPhone6,2",
+			@"iPhone7,2",
+			@"iPhone8,1",
+			@"iPhone8,4",
+			@"iPhone9,1",
+			@"iPhone9,3",
+			@"iPhone10,1",
+			@"iPhone10,4",
+			@"iPhone11,8",
+			@"iPhone12,1",
+			@"iPhone12,8",
+			@"iPad4,4",
+			@"iPad4,5",
+			@"iPad4,6",
+			@"iPad4,7",
+			@"iPad4,8",
+			@"iPad4,9",
+			@"iPad5,1",
+			@"iPad5,2",
+			@"iPad11,1",
+			@"iPad11,2",
+			@"iPod4,1",
+			@"iPod5,1",
+			@"iPod7,1",
+			@"iPod9,1",
+		] : @326,
+		@[
+			@"iPhone7,1",
+			@"iPhone8,2",
+			@"iPhone9,2",
+			@"iPhone9,4",
+			@"iPhone10,2",
+			@"iPhone10,5",
+		] : @401,
+		@[
+			@"iPhone10,3",
+			@"iPhone10,6",
+			@"iPhone11,2",
+			@"iPhone11,4",
+			@"iPhone11,6",
+			@"iPhone12,3",
+			@"iPhone12,5",
+		] : @458,
+	};
+}
+
+@end

+ 0 - 3
platform/iphone/display_layer.mm

@@ -124,11 +124,8 @@
 	}
 
 	if (context) {
-		[context release];
 		context = nil;
 	}
-
-	[super dealloc];
 }
 
 - (BOOL)createFramebuffer {

+ 30 - 155
platform/iphone/display_server_iphone.mm

@@ -32,8 +32,10 @@
 #import "app_delegate.h"
 #include "core/io/file_access_pack.h"
 #include "core/project_settings.h"
+#import "device_metrics.h"
 #import "godot_view.h"
 #include "ios.h"
+#import "native_video_view.h"
 #include "os_iphone.h"
 #import "view_controller.h"
 
@@ -41,120 +43,6 @@
 #import <sys/utsname.h>
 
 static const float kDisplayServerIPhoneAcceleration = 1;
-static NSDictionary *iOSModelToDPI = @{
-	@[
-		@"iPad1,1",
-		@"iPad2,1",
-		@"iPad2,2",
-		@"iPad2,3",
-		@"iPad2,4",
-	] : @132,
-	@[
-		@"iPhone1,1",
-		@"iPhone1,2",
-		@"iPhone2,1",
-		@"iPad2,5",
-		@"iPad2,6",
-		@"iPad2,7",
-		@"iPod1,1",
-		@"iPod2,1",
-		@"iPod3,1",
-	] : @163,
-	@[
-		@"iPad3,1",
-		@"iPad3,2",
-		@"iPad3,3",
-		@"iPad3,4",
-		@"iPad3,5",
-		@"iPad3,6",
-		@"iPad4,1",
-		@"iPad4,2",
-		@"iPad4,3",
-		@"iPad5,3",
-		@"iPad5,4",
-		@"iPad6,3",
-		@"iPad6,4",
-		@"iPad6,7",
-		@"iPad6,8",
-		@"iPad6,11",
-		@"iPad6,12",
-		@"iPad7,1",
-		@"iPad7,2",
-		@"iPad7,3",
-		@"iPad7,4",
-		@"iPad7,5",
-		@"iPad7,6",
-		@"iPad7,11",
-		@"iPad7,12",
-		@"iPad8,1",
-		@"iPad8,2",
-		@"iPad8,3",
-		@"iPad8,4",
-		@"iPad8,5",
-		@"iPad8,6",
-		@"iPad8,7",
-		@"iPad8,8",
-		@"iPad8,9",
-		@"iPad8,10",
-		@"iPad8,11",
-		@"iPad8,12",
-		@"iPad11,3",
-		@"iPad11,4",
-	] : @264,
-	@[
-		@"iPhone3,1",
-		@"iPhone3,2",
-		@"iPhone3,3",
-		@"iPhone4,1",
-		@"iPhone5,1",
-		@"iPhone5,2",
-		@"iPhone5,3",
-		@"iPhone5,4",
-		@"iPhone6,1",
-		@"iPhone6,2",
-		@"iPhone7,2",
-		@"iPhone8,1",
-		@"iPhone8,4",
-		@"iPhone9,1",
-		@"iPhone9,3",
-		@"iPhone10,1",
-		@"iPhone10,4",
-		@"iPhone11,8",
-		@"iPhone12,1",
-		@"iPhone12,8",
-		@"iPad4,4",
-		@"iPad4,5",
-		@"iPad4,6",
-		@"iPad4,7",
-		@"iPad4,8",
-		@"iPad4,9",
-		@"iPad5,1",
-		@"iPad5,2",
-		@"iPad11,1",
-		@"iPad11,2",
-		@"iPod4,1",
-		@"iPod5,1",
-		@"iPod7,1",
-		@"iPod9,1",
-	] : @326,
-	@[
-		@"iPhone7,1",
-		@"iPhone8,2",
-		@"iPhone9,2",
-		@"iPhone9,4",
-		@"iPhone10,2",
-		@"iPhone10,5",
-	] : @401,
-	@[
-		@"iPhone10,3",
-		@"iPhone10,6",
-		@"iPhone11,2",
-		@"iPhone11,4",
-		@"iPhone11,6",
-		@"iPhone12,3",
-		@"iPhone12,5",
-	] : @458,
-};
 
 DisplayServerIPhone *DisplayServerIPhone::get_singleton() {
 	return (DisplayServerIPhone *)DisplayServer::get_singleton();
@@ -383,8 +271,7 @@ void DisplayServerIPhone::update_gravity(float p_x, float p_y, float p_z) {
 	Input::get_singleton()->set_gravity(Vector3(p_x, p_y, p_z));
 };
 
-void DisplayServerIPhone::update_accelerometer(float p_x, float p_y,
-		float p_z) {
+void DisplayServerIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
 	// Found out the Z should not be negated! Pass as is!
 	Vector3 v_accelerometer = Vector3(
 			p_x / kDisplayServerIPhoneAcceleration,
@@ -392,39 +279,6 @@ void DisplayServerIPhone::update_accelerometer(float p_x, float p_y,
 			p_z / kDisplayServerIPhoneAcceleration);
 
 	Input::get_singleton()->set_accelerometer(v_accelerometer);
-
-	/*
-  if (p_x != last_accel.x) {
-      //printf("updating accel x %f\n", p_x);
-      InputEvent ev;
-      ev.type = InputEvent::JOYPAD_MOTION;
-      ev.device = 0;
-      ev.joy_motion.axis = JOY_ANALOG_0;
-      ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE);
-      last_accel.x = p_x;
-      queue_event(ev);
-  };
-  if (p_y != last_accel.y) {
-      //printf("updating accel y %f\n", p_y);
-      InputEvent ev;
-      ev.type = InputEvent::JOYPAD_MOTION;
-      ev.device = 0;
-      ev.joy_motion.axis = JOY_ANALOG_1;
-      ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE);
-      last_accel.y = p_y;
-      queue_event(ev);
-  };
-  if (p_z != last_accel.z) {
-      //printf("updating accel z %f\n", p_z);
-      InputEvent ev;
-      ev.type = InputEvent::JOYPAD_MOTION;
-      ev.device = 0;
-      ev.joy_motion.axis = JOY_ANALOG_2;
-      ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE);
-      last_accel.z = p_z;
-      queue_event(ev);
-  };
-  */
 };
 
 void DisplayServerIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
@@ -516,6 +370,8 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
 
 	NSString *string = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
 
+	NSDictionary *iOSModelToDPI = [GodotDeviceMetrics dpiList];
+
 	for (NSArray *keyArray in iOSModelToDPI) {
 		if ([keyArray containsObject:string]) {
 			NSNumber *value = iOSModelToDPI[keyArray];
@@ -523,7 +379,26 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
 		}
 	}
 
-	return 163;
+	// If device wasn't found in dictionary
+	// make a best guess from device metrics.
+	CGFloat scale = [UIScreen mainScreen].scale;
+
+	UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
+
+	switch (idiom) {
+		case UIUserInterfaceIdiomPad:
+			return scale == 2 ? 264 : 132;
+		case UIUserInterfaceIdiomPhone: {
+			if (scale == 3) {
+				CGFloat nativeScale = [UIScreen mainScreen].nativeScale;
+				return nativeScale == 3 ? 458 : 401;
+			}
+
+			return 326;
+		}
+		default:
+			return 72;
+	}
 }
 
 float DisplayServerIPhone::screen_get_scale(int p_screen) const {
@@ -716,7 +591,7 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri
 
 	String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
 
-	NSString *filePath = [[[NSString alloc] initWithUTF8String:file_path.utf8().get_data()] autorelease];
+	NSString *filePath = [[NSString alloc] initWithUTF8String:file_path.utf8().get_data()];
 	NSString *audioTrack = [NSString stringWithUTF8String:p_audio_track.utf8()];
 	NSString *subtitleTrack = [NSString stringWithUTF8String:p_subtitle_track.utf8()];
 
@@ -731,22 +606,22 @@ Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, Stri
 }
 
 bool DisplayServerIPhone::native_video_is_playing() const {
-	return [AppDelegate.viewController isVideoPlaying];
+	return [AppDelegate.viewController.videoView isVideoPlaying];
 }
 
 void DisplayServerIPhone::native_video_pause() {
 	if (native_video_is_playing()) {
-		[AppDelegate.viewController pauseVideo];
+		[AppDelegate.viewController.videoView pauseVideo];
 	}
 }
 
 void DisplayServerIPhone::native_video_unpause() {
-	[AppDelegate.viewController unpauseVideo];
+	[AppDelegate.viewController.videoView unpauseVideo];
 };
 
 void DisplayServerIPhone::native_video_stop() {
 	if (native_video_is_playing()) {
-		[AppDelegate.viewController stopVideo];
+		[AppDelegate.viewController.videoView stopVideo];
 	}
 }
 

+ 11 - 6
platform/iphone/game_center.mm

@@ -83,7 +83,12 @@ Error GameCenter::authenticate() {
 	// after the view is cancelled or the user logs in.  Or if the user's already logged in, it's
 	// called just once to confirm they're authenticated.  This is why no result needs to be specified
 	// in the presentViewController phase. In this case, more calls to this function will follow.
+	_weakify(root_controller);
+	_weakify(player);
 	player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
+		_strongify(root_controller);
+		_strongify(player);
+
 		if (controller) {
 			[root_controller presentViewController:controller animated:YES completion:nil];
 		} else {
@@ -123,8 +128,8 @@ Error GameCenter::post_score(Dictionary p_score) {
 	float score = p_score["score"];
 	String category = p_score["category"];
 
-	NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
-	GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
+	NSString *cat_str = [[NSString alloc] initWithUTF8String:category.utf8().get_data()];
+	GKScore *reporter = [[GKScore alloc] initWithLeaderboardIdentifier:cat_str];
 	reporter.value = score;
 
 	ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
@@ -152,8 +157,8 @@ Error GameCenter::award_achievement(Dictionary p_params) {
 	String name = p_params["name"];
 	float progress = p_params["progress"];
 
-	NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
-	GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:name_str] autorelease];
+	NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
+	GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:name_str];
 	ERR_FAIL_COND_V(!achievement, FAILED);
 
 	ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
@@ -297,7 +302,7 @@ Error GameCenter::show_game_center(Dictionary p_params) {
 		}
 	}
 
-	GKGameCenterViewController *controller = [[[GKGameCenterViewController alloc] init] autorelease];
+	GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
 	ERR_FAIL_COND_V(!controller, FAILED);
 
 	ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
@@ -309,7 +314,7 @@ Error GameCenter::show_game_center(Dictionary p_params) {
 		controller.leaderboardIdentifier = nil;
 		if (p_params.has("leaderboard_name")) {
 			String name = p_params["leaderboard_name"];
-			NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
+			NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
 			controller.leaderboardIdentifier = name_str;
 		}
 	}

+ 0 - 4
platform/iphone/godot_iphone.mm

@@ -49,10 +49,8 @@ int add_path(int p_argc, char **p_args) {
 	}
 
 	p_args[p_argc++] = (char *)"--path";
-	[str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo
 	p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
 	p_args[p_argc] = NULL;
-	[str release];
 
 	return p_argc;
 };
@@ -68,9 +66,7 @@ int add_cmdline(int p_argc, char **p_args) {
 		if (!str) {
 			continue;
 		}
-		[str retain]; // @todo delete these at some point
 		p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
-		[str release];
 	};
 
 	p_args[p_argc] = NULL;

+ 6 - 12
platform/iphone/godot_view.mm

@@ -145,8 +145,6 @@ static const int max_touches = 8;
 	if (self.delayGestureRecognizer) {
 		self.delayGestureRecognizer = nil;
 	}
-
-	[super dealloc];
 }
 
 - (void)godot_commonInit {
@@ -156,7 +154,7 @@ static const int max_touches = 8;
 
 	// Configure and start accelerometer
 	if (!self.motionManager) {
-		self.motionManager = [[[CMMotionManager alloc] init] autorelease];
+		self.motionManager = [[CMMotionManager alloc] init];
 		if (self.motionManager.deviceMotionAvailable) {
 			self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
 			[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
@@ -169,7 +167,6 @@ static const int max_touches = 8;
 	GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
 	self.delayGestureRecognizer = gestureRecognizer;
 	[self addGestureRecognizer:self.delayGestureRecognizer];
-	[gestureRecognizer release];
 }
 
 - (void)stopRendering {
@@ -204,14 +201,11 @@ static const int max_touches = 8;
 	if (self.useCADisplayLink) {
 		self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
 
-		//        if (@available(iOS 10, *)) {
-		self.displayLink.preferredFramesPerSecond = (NSInteger)(1.0 / self.renderingInterval);
-		//        } else {
-		//            // Approximate frame rate
-		//            // assumes device refreshes at 60 fps
-		//            int frameInterval = (int)floor(self.renderingInterval * 60.0f);
-		//            [self.displayLink setFrameInterval:frameInterval];
-		//        }
+		// Approximate frame rate
+		// assumes device refreshes at 60 fps
+		int displayFPS = (NSInteger)(1.0 / self.renderingInterval);
+
+		self.displayLink.preferredFramesPerSecond = displayFPS;
 
 		// Setup DisplayLink in main thread
 		[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

+ 0 - 7
platform/iphone/godot_view_gesture_recognizer.mm

@@ -83,8 +83,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
 	if (self.delayedEvent) {
 		self.delayedEvent = nil;
 	}
-
-	[super dealloc];
 }
 
 - (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
@@ -116,7 +114,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 	NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
 	[self delayTouches:cleared andEvent:event];
-	[cleared release];
 }
 
 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
@@ -137,17 +134,14 @@ const CGFloat kGLGestureMovementDistance = 0.5;
 			if (distance > kGLGestureMovementDistance) {
 				[self.delayTimer fire];
 				[self.view touchesMoved:cleared withEvent:event];
-				[cleared release];
 				return;
 			}
 		}
 
-		[cleared release];
 		return;
 	}
 
 	[self.view touchesMoved:cleared withEvent:event];
-	[cleared release];
 }
 
 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
@@ -155,7 +149,6 @@ const CGFloat kGLGestureMovementDistance = 0.5;
 
 	NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
 	[self.view touchesEnded:cleared withEvent:event];
-	[cleared release];
 }
 
 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {

+ 7 - 7
platform/iphone/icloud.mm

@@ -150,7 +150,7 @@ Variant nsobject_to_variant(NSObject *object) {
 
 NSObject *variant_to_nsobject(Variant v) {
 	if (v.get_type() == Variant::STRING) {
-		return [[[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()] autorelease];
+		return [[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()];
 	} else if (v.get_type() == Variant::FLOAT) {
 		return [NSNumber numberWithDouble:(double)v];
 	} else if (v.get_type() == Variant::INT) {
@@ -158,11 +158,11 @@ NSObject *variant_to_nsobject(Variant v) {
 	} else if (v.get_type() == Variant::BOOL) {
 		return [NSNumber numberWithBool:BOOL((bool)v)];
 	} else if (v.get_type() == Variant::DICTIONARY) {
-		NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
+		NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
 		Dictionary dic = v;
 		Array keys = dic.keys();
 		for (int i = 0; i < keys.size(); ++i) {
-			NSString *key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease];
+			NSString *key = [[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()];
 			NSObject *value = variant_to_nsobject(dic[keys[i]]);
 
 			if (key == NULL || value == NULL) {
@@ -173,7 +173,7 @@ NSObject *variant_to_nsobject(Variant v) {
 		}
 		return result;
 	} else if (v.get_type() == Variant::ARRAY) {
-		NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
+		NSMutableArray *result = [[NSMutableArray alloc] init];
 		Array arr = v;
 		for (int i = 0; i < arr.size(); ++i) {
 			NSObject *value = variant_to_nsobject(arr[i]);
@@ -195,7 +195,7 @@ NSObject *variant_to_nsobject(Variant v) {
 }
 
 Error ICloud::remove_key(String p_param) {
-	NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
+	NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
 
 	NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
 
@@ -217,7 +217,7 @@ Array ICloud::set_key_values(Dictionary p_params) {
 		String variant_key = keys[i];
 		Variant variant_value = p_params[variant_key];
 
-		NSString *key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease];
+		NSString *key = [[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()];
 		if (key == NULL) {
 			error_keys.push_back(variant_key);
 			continue;
@@ -238,7 +238,7 @@ Array ICloud::set_key_values(Dictionary p_params) {
 }
 
 Variant ICloud::get_key_value(String p_param) {
-	NSString *key = [[[NSString alloc] initWithUTF8String:p_param.utf8().get_data()] autorelease];
+	NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
 	NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
 
 	if (![[store dictionaryRepresentation] objectForKey:key]) {

+ 11 - 30
platform/iphone/in_app_store.mm

@@ -56,7 +56,6 @@ static NSArray *latestProducts;
 	[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
 	[numberFormatter setLocale:self.priceLocale];
 	NSString *formattedString = [numberFormatter stringFromNumber:self.price];
-	[numberFormatter release];
 	return formattedString;
 }
 
@@ -124,8 +123,6 @@ void InAppStore::_bind_methods() {
 	ret["invalid_ids"] = invalid_ids;
 
 	InAppStore::get_singleton()->_post_event(ret);
-
-	[request release];
 };
 
 @end
@@ -136,14 +133,14 @@ Error InAppStore::request_product_info(Dictionary p_params) {
 	PackedStringArray pids = p_params["product_ids"];
 	printf("************ request product info! %i\n", pids.size());
 
-	NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease];
+	NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()];
 	for (int i = 0; i < pids.size(); i++) {
 		printf("******** adding %s to product list\n", pids[i].utf8().get_data());
-		NSString *pid = [[[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()] autorelease];
+		NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()];
 		[array addObject:pid];
 	};
 
-	NSSet *products = [[[NSSet alloc] initWithArray:array] autorelease];
+	NSSet *products = [[NSSet alloc] initWithArray:array];
 	SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products];
 
 	ProductsDelegate *delegate = [[ProductsDelegate alloc] init];
@@ -183,30 +180,14 @@ Error InAppStore::restore_purchases() {
 				ret["transaction_id"] = transactionId;
 
 				NSData *receipt = nil;
-				int sdk_version = 6;
-
-				if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
-					NSURL *receiptFileURL = nil;
-					NSBundle *bundle = [NSBundle mainBundle];
-					if ([bundle respondsToSelector:@selector(appStoreReceiptURL)]) {
-						// Get the transaction receipt file path location in the app bundle.
-						receiptFileURL = [bundle appStoreReceiptURL];
-
-						// Read in the contents of the transaction file.
-						receipt = [NSData dataWithContentsOfURL:receiptFileURL];
-						sdk_version = 7;
+				int sdk_version = [[[UIDevice currentDevice] systemVersion] intValue];
 
-					} else {
-						// Fall back to deprecated transaction receipt,
-						// which is still available in iOS 7.
+				NSBundle *bundle = [NSBundle mainBundle];
+				// Get the transaction receipt file path location in the app bundle.
+				NSURL *receiptFileURL = [bundle appStoreReceiptURL];
 
-						// Use SKPaymentTransaction's transactionReceipt.
-						receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
-					}
-
-				} else {
-					receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
-				}
+				// Read in the contents of the transaction file.
+				receipt = [NSData dataWithContentsOfURL:receiptFileURL];
 
 				NSString *receipt_to_send = nil;
 				if (receipt != nil) {
@@ -265,7 +246,7 @@ Error InAppStore::purchase(Dictionary p_params) {
 	printf("purchasing!\n");
 	ERR_FAIL_COND_V(!p_params.has("product_id"), ERR_INVALID_PARAMETER);
 
-	NSString *pid = [[[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()] autorelease];
+	NSString *pid = [[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()];
 
 	SKProduct *product = nil;
 
@@ -307,7 +288,7 @@ void InAppStore::_post_event(Variant p_event) {
 
 void InAppStore::_record_purchase(String product_id) {
 	String skey = "purchased/" + product_id;
-	NSString *key = [[[NSString alloc] initWithUTF8String:skey.utf8().get_data()] autorelease];
+	NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()];
 	[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
 	[[NSUserDefaults standardUserDefaults] synchronize];
 };

+ 3 - 16
platform/iphone/ios.mm

@@ -30,6 +30,7 @@
 
 #include "ios.h"
 #import "app_delegate.h"
+#import "view_controller.h"
 #import <UIKit/UIKit.h>
 #include <sys/sysctl.h>
 
@@ -68,23 +69,9 @@ String iOS::get_model() const {
 }
 
 String iOS::get_rate_url(int p_app_id) const {
-	String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
-	String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID";
-	String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
+	String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
 
-	//ios7 before
-	String ret = templ;
-
-	if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1) {
-		// iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
-		ret = templ_iOS7;
-	} else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
-		// iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
-		ret = templ_iOS8;
-	}
-
-	// ios7 for everything?
-	ret = templ_iOS7.replace("APP_ID", String::num(p_app_id));
+	String ret = app_url_path.replace("APP_ID", String::num(p_app_id));
 
 	printf("returning rate url %s\n", ret.utf8().get_data());
 	return ret;

+ 33 - 68
platform/iphone/joypad_iphone.mm

@@ -131,8 +131,6 @@ void JoypadIPhone::start_processing() {
 
 - (void)dealloc {
 	[self finishObserving];
-
-	[super dealloc];
 }
 
 - (int)getJoyIdForController:(GCController *)controller {
@@ -251,8 +249,13 @@ void JoypadIPhone::start_processing() {
 		// The extended gamepad profile has all the input you could possibly find on
 		// a gamepad but will only be active if your gamepad actually has all of
 		// these...
-		controller.extendedGamepad.valueChangedHandler = ^(
-				GCExtendedGamepad *gamepad, GCControllerElement *element) {
+		_weakify(self);
+		_weakify(controller);
+
+		controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
+			_strongify(self);
+			_strongify(controller);
+
 			int joy_id = [self getJoyIdForController:controller];
 
 			if (element == gamepad.buttonA) {
@@ -304,71 +307,33 @@ void JoypadIPhone::start_processing() {
 				Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
 			};
 		};
+	} else if (controller.microGamepad != nil) {
+		// micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
+		_weakify(self);
+		_weakify(controller);
+
+		controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+			_strongify(self);
+			_strongify(controller);
+
+			int joy_id = [self getJoyIdForController:controller];
+
+			if (element == gamepad.buttonA) {
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+						gamepad.buttonA.isPressed);
+			} else if (element == gamepad.buttonX) {
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+						gamepad.buttonX.isPressed);
+			} else if (element == gamepad.dpad) {
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+						gamepad.dpad.up.isPressed);
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+						gamepad.dpad.down.isPressed);
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT, gamepad.dpad.left.isPressed);
+				Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT, gamepad.dpad.right.isPressed);
+			};
+		};
 	}
-	//    else if (controller.gamepad != nil) {
-	//        // gamepad is the standard profile with 4 buttons, shoulder buttons and a
-	//        // D-pad
-	//        controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad,
-	//                GCControllerElement *element) {
-	//            int joy_id = [self getJoyIdForController:controller];
-	//
-	//            if (element == gamepad.buttonA) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
-	//                        gamepad.buttonA.isPressed);
-	//            } else if (element == gamepad.buttonB) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
-	//                        gamepad.buttonB.isPressed);
-	//            } else if (element == gamepad.buttonX) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
-	//                        gamepad.buttonX.isPressed);
-	//            } else if (element == gamepad.buttonY) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
-	//                        gamepad.buttonY.isPressed);
-	//            } else if (element == gamepad.leftShoulder) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
-	//                        gamepad.leftShoulder.isPressed);
-	//            } else if (element == gamepad.rightShoulder) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
-	//                        gamepad.rightShoulder.isPressed);
-	//            } else if (element == gamepad.dpad) {
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
-	//                        gamepad.dpad.up.isPressed);
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
-	//                        gamepad.dpad.down.isPressed);
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
-	//                        gamepad.dpad.left.isPressed);
-	//                Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
-	//                        gamepad.dpad.right.isPressed);
-	//            };
-	//        };
-	//#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+,
-	//        // while we are setting that as the minimum, seems our
-	//        // build environment doesn't like it
-	//    } else if (controller.microGamepad != nil) {
-	//        // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
-	//        controller.microGamepad.valueChangedHandler =
-	//                ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
-	//                    int joy_id = [self getJoyIdForController:controller];
-	//
-	//                    if (element == gamepad.buttonA) {
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
-	//                                gamepad.buttonA.isPressed);
-	//                    } else if (element == gamepad.buttonX) {
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
-	//                                gamepad.buttonX.isPressed);
-	//                    } else if (element == gamepad.dpad) {
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
-	//                                gamepad.dpad.up.isPressed);
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
-	//                                gamepad.dpad.down.isPressed);
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
-	//                                gamepad.dpad.left.isPressed);
-	//                        Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
-	//                                gamepad.dpad.right.isPressed);
-	//                    };
-	//                };
-	//#endif
-	//    };
 
 	///@TODO need to add support for controller.motion which gives us access to
 	/// the orientation of the device (if supported)

+ 41 - 0
platform/iphone/native_video_view.h

@@ -0,0 +1,41 @@
+/*************************************************************************/
+/*  native_video_view.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 <UIKit/UIKit.h>
+
+@interface GodotNativeVideoView : UIView
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
+- (BOOL)isVideoPlaying;
+- (void)pauseVideo;
+- (void)unpauseVideo;
+- (void)stopVideo;
+
+@end

+ 260 - 0
platform/iphone/native_video_view.m

@@ -0,0 +1,260 @@
+/*************************************************************************/
+/*  native_video_view.m                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 "native_video_view.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface GodotNativeVideoView ()
+
+@property(strong, nonatomic) AVAsset *avAsset;
+@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
+@property(strong, nonatomic) AVPlayer *avPlayer;
+@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
+@property(assign, nonatomic) CMTime videoCurrentTime;
+@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
+
+@end
+
+@implementation GodotNativeVideoView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+	self = [super initWithFrame:frame];
+
+	if (self) {
+		[self godot_commonInit];
+	}
+
+	return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+	self = [super initWithCoder:coder];
+
+	if (self) {
+		[self godot_commonInit];
+	}
+
+	return self;
+}
+
+- (void)godot_commonInit {
+	self.isVideoCurrentlyPlaying = NO;
+	self.videoCurrentTime = kCMTimeZero;
+
+	[self observeVideoAudio];
+}
+
+- (void)observeVideoAudio {
+	printf("******** adding observer for sound routing changes\n");
+	[[NSNotificationCenter defaultCenter]
+			addObserver:self
+			   selector:@selector(audioRouteChangeListenerCallback:)
+				   name:AVAudioSessionRouteChangeNotification
+				 object:nil];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+	if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
+		[self handleVideoOrPlayerStatus];
+	}
+
+	if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
+		[self handleVideoPlayRate];
+	}
+}
+
+// MARK: Video Audio
+
+- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
+	printf("*********** route changed!\n");
+	NSDictionary *interuptionDict = notification.userInfo;
+
+	NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
+
+	switch (routeChangeReason) {
+		case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
+			NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+			NSLog(@"Headphone/Line plugged in");
+		} break;
+		case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
+			NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+			NSLog(@"Headphone/Line was pulled. Resuming video play....");
+			if ([self isVideoPlaying]) {
+				dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+					[self.avPlayer play]; // NOTE: change this line according your current player implementation
+					NSLog(@"resumed play");
+				});
+			}
+		} break;
+		case AVAudioSessionRouteChangeReasonCategoryChange: {
+			// called at start - also when other audio wants to play
+			NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
+		} break;
+	}
+}
+
+// MARK: Native Video Player
+
+- (void)handleVideoOrPlayerStatus {
+	if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
+		[self stopVideo];
+	}
+
+	if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
+		//        NSLog(@"time: %@", self.video_current_time);
+		[self.avPlayer seekToTime:self.videoCurrentTime];
+		self.videoCurrentTime = kCMTimeZero;
+	}
+}
+
+- (void)handleVideoPlayRate {
+	NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
+	if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
+		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+			[self.avPlayer play]; // NOTE: change this line according your current player implementation
+			NSLog(@"resumed play");
+		});
+
+		NSLog(@" . . . PAUSED (or just started)");
+	}
+}
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+	self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
+
+	self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
+	[self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
+
+	self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
+	self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
+
+	[self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
+	[[NSNotificationCenter defaultCenter]
+			addObserver:self
+			   selector:@selector(playerItemDidReachEnd:)
+				   name:AVPlayerItemDidPlayToEndTimeNotification
+				 object:[self.avPlayer currentItem]];
+
+	[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
+
+	[self.avPlayerLayer setFrame:self.bounds];
+	[self.layer addSublayer:self.avPlayerLayer];
+	[self.avPlayer play];
+
+	AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+
+	NSMutableArray *allAudioParams = [NSMutableArray array];
+	for (id track in audioGroup.options) {
+		NSString *language = [[track locale] localeIdentifier];
+		NSLog(@"subtitle lang: %@", language);
+
+		if ([language isEqualToString:audioTrack]) {
+			AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
+			[audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
+			[audioInputParams setTrackID:[track trackID]];
+			[allAudioParams addObject:audioInputParams];
+
+			AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
+			[audioMix setInputParameters:allAudioParams];
+
+			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
+			[self.avPlayer.currentItem setAudioMix:audioMix];
+
+			break;
+		}
+	}
+
+	AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+	NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
+
+	for (id track in useableTracks) {
+		NSString *language = [[track locale] localeIdentifier];
+		NSLog(@"subtitle lang: %@", language);
+
+		if ([language isEqualToString:subtitleTrack]) {
+			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
+			break;
+		}
+	}
+
+	self.isVideoCurrentlyPlaying = YES;
+
+	return true;
+}
+
+- (BOOL)isVideoPlaying {
+	if (self.avPlayer.error) {
+		printf("Error during playback\n");
+	}
+	return (self.avPlayer.rate > 0 && !self.avPlayer.error);
+}
+
+- (void)pauseVideo {
+	self.videoCurrentTime = self.avPlayer.currentTime;
+	[self.avPlayer pause];
+	self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)unpauseVideo {
+	[self.avPlayer play];
+	self.isVideoCurrentlyPlaying = YES;
+}
+
+- (void)playerItemDidReachEnd:(NSNotification *)notification {
+	[self stopVideo];
+}
+
+- (void)finishPlayingVideo {
+	[self.avPlayer pause];
+	[self.avPlayerLayer removeFromSuperlayer];
+	self.avPlayerLayer = nil;
+
+	if (self.avPlayerItem) {
+		[self.avPlayerItem removeObserver:self forKeyPath:@"status"];
+		self.avPlayerItem = nil;
+	}
+
+	if (self.avPlayer) {
+		[self.avPlayer removeObserver:self forKeyPath:@"status"];
+		self.avPlayer = nil;
+	}
+
+	self.avAsset = nil;
+
+	self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)stopVideo {
+	[self finishPlayingVideo];
+
+	[self removeFromSuperview];
+}
+
+@end

+ 0 - 5
platform/iphone/os_iphone.mm

@@ -271,7 +271,6 @@ String OSIPhone::get_model_name() const {
 Error OSIPhone::shell_open(String p_uri) {
 	NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
 	NSURL *url = [NSURL URLWithString:urlPath];
-	[urlPath release];
 
 	if (![[UIApplication sharedApplication] canOpenURL:url]) {
 		return ERR_CANT_OPEN;
@@ -279,11 +278,7 @@ Error OSIPhone::shell_open(String p_uri) {
 
 	printf("opening url %s\n", p_uri.utf8().get_data());
 
-	//    if (@available(iOS 10, *)) {
 	[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
-	//    } else {
-	//        [[UIApplication sharedApplication] openURL:url];
-	//    }
 
 	return OK;
 };

+ 7 - 0
platform/iphone/platform_config.h

@@ -33,3 +33,10 @@
 #define PLATFORM_REFCOUNT
 
 #define PTHREAD_RENAME_SELF
+
+#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
+#define _strongify(var)                                      \
+	_Pragma("clang diagnostic push")                         \
+			_Pragma("clang diagnostic ignored \"-Wshadow\"") \
+					__strong typeof(var) var = GDWeak_##var; \
+	_Pragma("clang diagnostic pop")

+ 3 - 5
platform/iphone/view_controller.h

@@ -32,17 +32,15 @@
 #import <UIKit/UIKit.h>
 
 @class GodotView;
+@class GodotNativeVideoView;
 
 @interface ViewController : UIViewController <GKGameCenterControllerDelegate>
 
-- (GodotView *)godotView;
+@property(nonatomic, readonly, strong) GodotView *godotView;
+@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
 
 // MARK: Native Video Player
 
 - (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-- (BOOL)isVideoPlaying;
-- (void)pauseVideo;
-- (void)unpauseVideo;
-- (void)stopVideo;
 
 @end

+ 14 - 195
platform/iphone/view_controller.mm

@@ -33,6 +33,7 @@
 #include "display_server_iphone.h"
 #import "godot_view.h"
 #import "godot_view_renderer.h"
+#import "native_video_view.h"
 #include "os_iphone.h"
 
 #import <GameController/GameController.h>
@@ -40,16 +41,7 @@
 @interface ViewController ()
 
 @property(strong, nonatomic) GodotViewRenderer *renderer;
-
-// TODO: separate view to handle video
-// AVPlayer-related properties
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-@property(assign, nonatomic) CMTime videoCurrentTime;
-@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
-@property(assign, nonatomic) BOOL videoHasFoundError;
+@property(strong, nonatomic) GodotNativeVideoView *videoView;
 
 @end
 
@@ -67,9 +59,6 @@
 	self.view = view;
 
 	view.renderer = self.renderer;
-
-	[renderer release];
-	[view release];
 }
 
 - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@@ -93,9 +82,7 @@
 }
 
 - (void)godot_commonInit {
-	self.isVideoCurrentlyPlaying = NO;
-	self.videoCurrentTime = kCMTimeZero;
-	self.videoHasFoundError = false;
+	// Initialize view controller values.
 }
 
 - (void)didReceiveMemoryWarning {
@@ -107,7 +94,6 @@
 	[super viewDidLoad];
 
 	[self observeKeyboard];
-	[self observeAudio];
 
 	if (@available(iOS 11.0, *)) {
 		[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
@@ -128,33 +114,13 @@
 				 object:nil];
 }
 
-- (void)observeAudio {
-	printf("******** adding observer for sound routing changes\n");
-	[[NSNotificationCenter defaultCenter]
-			addObserver:self
-			   selector:@selector(audioRouteChangeListenerCallback:)
-				   name:AVAudioSessionRouteChangeNotification
-				 object:nil];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
-	if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
-		[self handleVideoOrPlayerStatus];
-	}
-
-	if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
-		[self handleVideoPlayRate];
-	}
-}
-
 - (void)dealloc {
-	[self stopVideo];
+	[self.videoView stopVideo];
 
+	self.videoView = nil;
 	self.renderer = nil;
 
 	[[NSNotificationCenter defaultCenter] removeObserver:self];
-
-	[super dealloc];
 }
 
 // MARK: Orientation
@@ -233,166 +199,19 @@
 	}
 }
 
-// MARK: Audio
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
-	printf("*********** route changed!\n");
-	NSDictionary *interuptionDict = notification.userInfo;
-
-	NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
-	switch (routeChangeReason) {
-		case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
-			NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
-			NSLog(@"Headphone/Line plugged in");
-		} break;
-		case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
-			NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
-			NSLog(@"Headphone/Line was pulled. Resuming video play....");
-			if ([self isVideoPlaying]) {
-				dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
-					[self.avPlayer play]; // NOTE: change this line according your current player implementation
-					NSLog(@"resumed play");
-				});
-			}
-		} break;
-		case AVAudioSessionRouteChangeReasonCategoryChange: {
-			// called at start - also when other audio wants to play
-			NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
-		} break;
-	}
-}
-
 // MARK: Native Video Player
 
-- (void)handleVideoOrPlayerStatus {
-	if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
-		[self stopVideo];
-		self.videoHasFoundError = true;
-	}
-
-	if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
-		//        NSLog(@"time: %@", self.video_current_time);
-		[self.avPlayer seekToTime:self.videoCurrentTime];
-		self.videoCurrentTime = kCMTimeZero;
-	}
-}
-
-- (void)handleVideoPlayRate {
-	NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
-	if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
-		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
-			[self.avPlayer play]; // NOTE: change this line according your current player implementation
-			NSLog(@"resumed play");
-		});
-
-		NSLog(@" . . . PAUSED (or just started)");
-	}
-}
-
 - (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
-	self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
-
-	self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
-	[self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
-
-	self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
-	self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
-
-	[self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
-	[[NSNotificationCenter defaultCenter]
-			addObserver:self
-			   selector:@selector(playerItemDidReachEnd:)
-				   name:AVPlayerItemDidPlayToEndTimeNotification
-				 object:[self.avPlayer currentItem]];
-
-	[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
-	[self.avPlayerLayer setFrame:self.view.bounds];
-	[self.view.layer addSublayer:self.avPlayerLayer];
-	[self.avPlayer play];
-
-	AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
-	NSMutableArray *allAudioParams = [NSMutableArray array];
-	for (id track in audioGroup.options) {
-		NSString *language = [[track locale] localeIdentifier];
-		NSLog(@"subtitle lang: %@", language);
-
-		if ([language isEqualToString:audioTrack]) {
-			AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
-			[audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
-			[audioInputParams setTrackID:[track trackID]];
-			[allAudioParams addObject:audioInputParams];
-
-			AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
-			[audioMix setInputParameters:allAudioParams];
-
-			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
-			[self.avPlayer.currentItem setAudioMix:audioMix];
-
-			break;
-		}
-	}
-
-	AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
-	NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
-	for (id track in useableTracks) {
-		NSString *language = [[track locale] localeIdentifier];
-		NSLog(@"subtitle lang: %@", language);
-
-		if ([language isEqualToString:subtitleTrack]) {
-			[self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
-			break;
-		}
-	}
-
-	self.isVideoCurrentlyPlaying = YES;
-
-	return true;
-}
-
-- (BOOL)isVideoPlaying {
-	if (self.avPlayer.error) {
-		printf("Error during playback\n");
-	}
-	return (self.avPlayer.rate > 0 && !self.avPlayer.error);
-}
-
-- (void)pauseVideo {
-	self.videoCurrentTime = self.avPlayer.currentTime;
-	[self.avPlayer pause];
-	self.isVideoCurrentlyPlaying = NO;
-}
-
-- (void)unpauseVideo {
-	[self.avPlayer play];
-	self.isVideoCurrentlyPlaying = YES;
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
-	[self stopVideo];
-}
-
-- (void)stopVideo {
-	[self.avPlayer pause];
-	[self.avPlayerLayer removeFromSuperlayer];
-	self.avPlayerLayer = nil;
-
-	if (self.avPlayerItem) {
-		[self.avPlayerItem removeObserver:self forKeyPath:@"status"];
-		self.avPlayerItem = nil;
-	}
-
-	if (self.avPlayer) {
-		[self.avPlayer removeObserver:self forKeyPath:@"status"];
-		self.avPlayer = nil;
+	// If we are showing some video already, reuse existing view for new video.
+	if (self.videoView) {
+		return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
+	} else {
+		// Create autoresizing view for video playback.
+		GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
+		videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth & UIViewAutoresizingFlexibleHeight;
+		[self.view addSubview:videoView];
+		return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
 	}
-
-	self.avAsset = nil;
-
-	self.isVideoCurrentlyPlaying = NO;
 }
 
 // MARK: Delegates

+ 2 - 4
platform/iphone/vulkan_context_iphone.mm

@@ -35,14 +35,12 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
 	return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
 }
 
-Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id,
-		CALayer *p_metal_layer, int p_width,
-		int p_height) {
+Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height) {
 	VkIOSSurfaceCreateInfoMVK createInfo;
 	createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
 	createInfo.pNext = NULL;
 	createInfo.flags = 0;
-	createInfo.pView = p_metal_layer;
+	createInfo.pView = (__bridge const void *)p_metal_layer;
 
 	VkSurfaceKHR surface;
 	VkResult err =