Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

use @main in Xcode 12

I want to run this code on iOS 13 and above how should I fix this error? I want to make this code could run on iOS 13 too.

@available(iOS 14.0, *)
@main

struct WeatherProApp: App {
  @Environment(\.scenePhase) private var scenePhase
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  
  var body: some Scene {
    WindowGroup{
      let fetcher = WeatherFetcher()
      let viewModel = WeeklyWeatherViewModel(weatherFethcer: fetcher)
      WeeklyWeatherView(viewModel: viewModel)
    }
    .onChange(of: scenePhase) { (newScenePhase) in
      switch newScenePhase {
      case .active:
        print("scene is now active!")
      case .inactive:
        print("scene is now inactive!")
      case .background:
        print("scene is now in the background!")
      @unknown default:
        print("Apple must have added something new!")
      }
    }
  }
}

but it shows me this error

Error Image

like image 698
Alireza12t Avatar asked Jul 16 '20 12:07

Alireza12t


People also ask

What does @main do in Xcode?

Xcode 12 ships with Swift 5.3 which introduces the @main attribute as a general purpose way of marking the entry point of a Swift program. You use it in place of Apple specific attributes like @UIApplicationMain and @NSApplicationMain . If you're writing iOS or macOS apps you can probably just accept that and move on.

What is @main in Swift?

In all programs you always have an entry point, a place where your app should start from, and swift is not an exception to that. If you remember the very old days we where having main.m as below: main.m. int main(int argc, char *argv[])

What is @UIApplicationMain?

UIApplicationMain(_:_:_:_:) Creates the application object and the application delegate and sets up the event cycle.

What's new in Xcode 12?

Xcode 12 now encrypts the connection between Xcode and paired devices, protecting against an attacker in a privileged network position executing arbitrary code on connected iOS, iPadOS, watchOS, or tvOS devices during a remote debug session. (60386733)

How to use @main attribute on <ios14>?

That error message is very clear. You cannot use the @main attribute on <iOS14, only on iOS14+. If you want to use it, you either need to bump your deployment target to iOS 14 or have 2 separate versions of your code, one with if #available (iOS 14) and one for the older versions.

What is the @main attribute in Swift 12?

} Xcode 12 ships with Swift 5.3 which introduces the @main attribute as a general purpose way of marking the entry point of a Swift program. You use it in place of Apple specific attributes like @UIApplicationMain and @NSApplicationMain.

What is @main in Swift and how to use it?

@main works similarly to a protocol -- when added, you must define a main () method that will serve as the entry point for your Swift code. Apart from that, @main works precisely like @UIApplicationMain.


2 Answers

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))
            }
        }
    }
like image 55
Ugo Marinelli Avatar answered Sep 25 '22 02:09

Ugo Marinelli


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:

@main
struct 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.

like image 41
the.blaggy Avatar answered Sep 24 '22 02:09

the.blaggy