use @main in Xcode 12 use @main in Xcode 12 swift swift

use @main in Xcode 12


Actually you can use the @main attribute in pre iOS 14 but you need an alternative AppDelegate and SceneDelegate (you can copy these two delegate classes from iOS 13 Xcode projects) and you have to do some extra wrapping.

First you have to apply the @main attribute in the following way to a struct with a main function which decides depending on the iOS version whether to use the WeatherProApp struct or the AppDelegate class to launch:

@mainstruct WeatherProAppWrapper {    static func main() {        if #available(iOS 14.0, *) {            WeatherProApp.main()        }        else {            UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))        }    }}

Afterwards you can use the shown implementation from your question, just remove the @main attribute, only use @available(iOS 14.0, *). E.g.:

@available(iOS 14.0, *)struct WeatherProApp: App {    var body: some Scene {        WindowGroup{            ContentView()        }    }}

I'm not sure how familiar you're with UIKit but you have to do the same setup you did in your WindowGroup in the SceneDelegate class too.


Following @the.blaggy answer, here is how I managed to run my project on iOS 13:

  1. Create a SceneDelegate if you do not have one

SceneDelegate.swift

class SceneDelegate: UIResponder, UIWindowSceneDelegate {    var window: UIWindow?    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {        let contentView = ContentView()        // Use a UIHostingController as window root view controller.        if let windowScene = scene as? UIWindowScene {            let window = UIWindow(windowScene: windowScene)            window.rootViewController = UIHostingController(rootView: contentView)            self.window = window            window.makeKeyAndVisible()        }    }}
  1. Open your info.plist as Source Code and add those lines :

Info.plist

   <key>UIApplicationSceneManifest</key>       <dict>           <key>UIApplicationSupportsMultipleScenes</key>           <false/>           <key>UISceneConfigurations</key>           <dict>           <key>UIWindowSceneSessionRoleApplication</key>           <array>               <dict>                   <key>UISceneConfigurationName</key>                   <string>Default Configuration</string>                   <key>UISceneDelegateClassName</key>                   <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>               </dict>           </array>       </dict>   </dict>
  1. Add this in your WeatherProApp.swift

WeatherProApp.swift

    @main    struct WeatherProAppWrapper {        static func main() {            if #available(iOS 14.0, *) {                WeatherProApp.main()            }            else {                UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(SceneDelegate.self))            }        }    }


This might depend on other project code, but the following tested as works (Xcode 12b), so might be helpful.

The idea is to hide one wrapper inside another structure with availability checker:

@available(iOS 14.0, macOS 10.16, *)struct Testing_SwiftUI2AppHolder {    @main    struct Testing_SwiftUI2App: App {        var body: some Scene {            WindowGroup {                ContentView()            }        }    }}