I ended up finding a solution for UIKit through the hints from @jpdev001. The fix involves two key components:
Creating a hidden UIWindow that provides the necessary context for StoreKit operations. This window is kept at a lower window level but maintains key status.
Wrapping the presenting view controller in a UINavigationController, which iOS 18.2's StoreKit requires for proper purchase flow.
I created a reusable protocol and utility function that handles this automatically. The function takes care of creating the hidden window, managing the navigation controller setup, and setting the requested presentation styles.
| protocol Navigatable: UIViewController { |
| var displayedWindow: UIWindow? { get set } |
| } |
| |
| extension UIViewController { |
| func presentWithStoreKitSupport<T: Navigatable>( |
| from presentingView: UIViewController, |
| viewController: T, |
| presentationStyle: UIModalPresentationStyle, |
| isModalInPresentation: Bool = false, |
| transitionStyle: UIModalTransitionStyle? = nil, |
| animated: Bool = true |
| ) { |
| |
| let navigationController = UINavigationController(rootViewController: viewController) |
| navigationController.isNavigationBarHidden = true |
| navigationController.modalPresentationStyle = presentationStyle |
| navigationController.isModalInPresentation = isModalInPresentation |
| |
| if let transitionStyle = transitionStyle { |
| navigationController.modalTransitionStyle = transitionStyle |
| } |
| |
| |
| if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { |
| let purchaseWindow = UIWindow(windowScene: windowScene) |
| purchaseWindow.isHidden = true |
| purchaseWindow.windowLevel = .normal - 1 |
| purchaseWindow.rootViewController = UIViewController() |
| purchaseWindow.makeKeyAndVisible() |
| |
| |
| viewController.displayedWindow = purchaseWindow |
| |
| |
| presentingView.present(navigationController, animated: animated) |
| } |
| } |
| } |
The transition to the respective view can be implemented the following way:
| view.presentWithStoreKitSupport( |
| from: view, |
| viewController: navigatingView, |
| presentationStyle: .fullScreen |
| ) |
Using this approach for the views that include in-app purchases fixed the issue for me. The solution also still works for older iOS Versions (tested with iOS 15 an 17). Also tested on multiple physical devices (iPhone 12 Pro and 15 Pro).