From 2e08b54402500d3b5df74423cd074cd0bdf8d1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 10 Oct 2026 15:07:41 +0200 Subject: [PATCH] iOS: Move off deprecated APIs - Request an orientation update with setNeedsUpdateOfSupportedInterfaceOrientations on iOS 16+, where attemptRotationToDeviceOrientation is deprecated. - Open the document picker with UTTypes on iOS 14+, and present it from sharedViewController rather than through the deprecated keyWindow. - List cameras with a discovery session instead of devicesWithMediaType. - IS_IPAD() instead of UI_USER_INTERFACE_IDIOM(). Co-Authored-By: Claude Opus 5.5 --- CMakeLists.txt | 2 ++ Core/Util/DarwinFileSystemServices.mm | 36 ++++++++++++++------------- ios/AppDelegate.mm | 2 +- ios/CameraHelper.mm | 16 ++++++++++-- ios/main.mm | 9 ++++--- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dba88103ff..7d256ee130 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -968,6 +968,8 @@ elseif(IOS AND NOT LIBRETRO) if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/PhotosUI.framework") list(APPEND targetExtraLibs "-weak_framework PhotosUI") endif() + # Weak, since it's iOS 14+ and we still run on 13. + list(APPEND targetExtraLibs "-weak_framework UniformTypeIdentifiers") if(USE_IAP) # Adding the In-App Purchase capability in Xcode did this. There's no entitlement for it on iOS. list(APPEND targetExtraLibs "-framework StoreKit") diff --git a/Core/Util/DarwinFileSystemServices.mm b/Core/Util/DarwinFileSystemServices.mm index 1cb3bf694c..cade637187 100644 --- a/Core/Util/DarwinFileSystemServices.mm +++ b/Core/Util/DarwinFileSystemServices.mm @@ -32,6 +32,7 @@ #if __has_include() #include "../ios/ViewControllerCommon.h" #include +#include @interface DocumentPickerDelegate : NSObject @property DarwinDirectoryPanelCallback panelCallback; @@ -279,27 +280,28 @@ void DarwinFileSystemServices::presentDirectoryPanel( panelCallback(false, Path()); } #elif PPSSPP_PLATFORM(IOS) - UIViewController *rootViewController = UIApplication.sharedApplication - .keyWindow - .rootViewController; - - // get current window view controller + // The window's root view controller. + UIViewController *rootViewController = sharedViewController; if (!rootViewController) return; - NSMutableArray *types = [NSMutableArray array]; - UIDocumentPickerMode pickerMode = UIDocumentPickerModeOpen; - - if (allowDirectories) - [types addObject: (__bridge NSString *)kUTTypeFolder]; - if (allowFiles) { - [types addObject: (__bridge NSString *)kUTTypeItem]; - // NOTE: We do not want to copy files here - we handle it ourselves if needed. - // Previously this was Import mode. - pickerMode = UIDocumentPickerModeOpen; + // Open, not import - we don't want the files copied, we handle that ourselves if needed. + UIDocumentPickerViewController *pickerVC; + if (@available(iOS 14.0, *)) { + NSMutableArray *types = [NSMutableArray array]; + if (allowDirectories) + [types addObject:UTTypeFolder]; + if (allowFiles) + [types addObject:UTTypeItem]; + pickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:types]; + } else { + NSMutableArray *types = [NSMutableArray array]; + if (allowDirectories) + [types addObject:(__bridge NSString *)kUTTypeFolder]; + if (allowFiles) + [types addObject:(__bridge NSString *)kUTTypeItem]; + pickerVC = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:types inMode:UIDocumentPickerModeOpen]; } - - UIDocumentPickerViewController *pickerVC = [[UIDocumentPickerViewController alloc] initWithDocumentTypes: types inMode: pickerMode]; // What if you wanted to go to heaven, but then God showed you the next few lines? // serious note: have to do this, because __pickerDelegate has to stay retained as a class property __pickerDelegate = (void *)CFBridgingRetain([[DocumentPickerDelegate alloc] initWithCallback:panelCallback]); diff --git a/ios/AppDelegate.mm b/ios/AppDelegate.mm index 6af4f6e2af..f9ea87ac87 100644 --- a/ios/AppDelegate.mm +++ b/ios/AppDelegate.mm @@ -234,7 +234,7 @@ static std::vector GatherGameLibrary() { return UIInterfaceOrientationMaskLandscapeRight; case ROTATION_LOCKED_VERTICAL: case ROTATION_LOCKED_VERTICAL180: - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + if (IS_IPAD()) { // iPad supports both portrait orientations, so allow them. return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown; } diff --git a/ios/CameraHelper.mm b/ios/CameraHelper.mm index 5db050bf9a..8afce22999 100644 --- a/ios/CameraHelper.mm +++ b/ios/CameraHelper.mm @@ -14,9 +14,21 @@ @implementation CameraHelper +static NSArray *GetVideoDevices() { + NSArray *types = @[ + AVCaptureDeviceTypeBuiltInWideAngleCamera, + AVCaptureDeviceTypeBuiltInUltraWideCamera, + AVCaptureDeviceTypeBuiltInTelephotoCamera, + AVCaptureDeviceTypeBuiltInTrueDepthCamera, + ]; + return [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:types + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified].devices; +} + std::vector System_GetCameraDeviceList() { std::vector deviceList; - for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + for (AVCaptureDevice *device in GetVideoDevices()) { deviceList.push_back([device.localizedName UTF8String]); } return deviceList; @@ -76,7 +88,7 @@ NSString *getSelectedCamera() { AVCaptureDeviceInput *videoInput = nil; NSString *selectedCamera = getSelectedCamera(); - for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + for (AVCaptureDevice *device in GetVideoDevices()) { if ([device.localizedName isEqualToString:selectedCamera]) { videoInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; } diff --git a/ios/main.mm b/ios/main.mm index 62e9ceb22e..5959b34a3a 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -452,11 +452,14 @@ void System_Notify(SystemNotification notification) { }); break; case SystemNotification::ROTATE_UPDATED: - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ if (sharedViewController) { - // [sharedViewController setNeedsUpdateOfSupportedInterfaceOrientations]; INFO_LOG(Log::System, "Requesting device orientation update"); - [UIViewController attemptRotationToDeviceOrientation]; + if (@available(iOS 16.0, *)) { + [sharedViewController setNeedsUpdateOfSupportedInterfaceOrientations]; + } else { + [UIViewController attemptRotationToDeviceOrientation]; + } } }); break;