Unfortunately UIScreen.main
and UIScreen.mainScreen()
have both been deprecated since iOS 16.
Apple discourages the use of this symbol. Use a UIScreen instance found through context instead. For example, reference the screen that displays a view through the screen property on the window scene managing the window containing the view.
As usual the Apple document is great at telling you what not to do, but seldom give you examples of what to do. So after some searching and testing I came up with my own solution.
I created a helper class with static functions giving me access to the current scene and from there the window and screen.
This solution only works for single window apps such as iOS and will need some additional work for iPad and macOS apps.
My solution was derived from a combination of the responses in this thread. Both answers create a UIWindow?
variable in the main app entry.
The First Response
@main
struct DemoApp: App {
var window: UIWindow? {
guard let scene = UIApplication.shared.connectedScenes.first,
let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate,
let window = windowSceneDelegate.window else {
return nil
}
return window
}
[...]
}
The Second Response
@main
struct TestApp: App {
var window: UIWindow? {
guard let scene = UIApplication.shared.connectedScenes.first,
let windowScene = scene as? UIWindowScene else {
return nil
}
return .init(windowScene: windowScene)
}
}
I'm not in favor of the second response because it creates a new UIWindow
from the UIWindowScene
. You have access to an array of windows from the UIWindowScene
.
My first attempt at this solution is to create a helper and convenience functions, and then exposing those onto View
with convenience functions.
Helper
struct Application {
public static var scene: UIScene? {
guard let scene = UIApplication.shared.connectedScenes.first else {
return nil
}
return scene
}
public static var window: UIWindow? {
guard let scene = self.scene,
let delegate = scene.delegate as? UIWindowSceneDelegate,
let window = delegate.window else {
return nil
}
return window
}
public static var screen: UIScreen? {
guard let window = self.window else {
return nil
}
return window.screen
}
}
I chose to access to UIScene
from the delegate instead of casting UIScene
to UIWindowScene
because according to Apple's documentation for UIScene
it is:
An object that represents one instance of your app's user interface.
Reading Apple's documentation for UIWindowScene
convinced me that getting it through a delegate is the more reliable way. I am curious if using a UISceneConfiguration
entry in the info.plist is better?
Extension
extension View {
var scene: UIScene? {
return Application.scene
}
var window: UIWindow? {
return Application.window
}
var screen: UIScreen? {
return Application.screen
}
}
I'm not a seasoned Swift developer so I am confident there is a better solution. I wonder what a better solution might be? It seems that UIScreen.main
is an exceptionally common object it would be great to establish a new pattern for it's use.