【问题标题】:use @main in Xcode 12在 Xcode 12 中使用 @main
【发布时间】:2020-11-06 03:26:18
【问题描述】:

我想在 iOS 13 及更高版本上运行此代码我应该如何修复此错误? 我想让这段代码也可以在 iOS 13 上运行。

@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!")
      }
    }
  }
}

但它显示了这个错误

【问题讨论】:

  • 那个错误信息很清楚。您不能在 @main 属性,只能在 iOS14+ 上使用。如果你想使用它,你要么需要将你的部署目标升级到 iOS 14,要么有 2 个单独的代码版本,一个是 if #available(iOS 14),一个是旧版本。

标签: swift swiftui ios14 xcode12


【解决方案1】:

实际上,您可以在 iOS 14 之前使用 @main 属性,但您需要替代 AppDelegateSceneDelegate(您可以从 iOS 13 Xcode 项目中复制这两个委托类)并且您必须做一些额外的包装.

首先,您必须通过以下方式将@main 属性应用于具有main 函数的结构,该函数根据iOS 版本决定是使用WeatherProApp 结构还是AppDelegate 类来启动:

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

之后,您可以使用问题中显示的实现,只需删除@main 属性,仅使用@available(iOS 14.0, *)。例如:

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

我不确定您对 UIKit 的熟悉程度,但您也必须在 SceneDelegate 类的 WindowGroup 中进行相同的设置。

【讨论】:

  • 您能详细说明您需要在 SceneDelegate 中做什么吗?如果 Xcode 在 iOS 13 中恢复为 AppDelegate 为什么我们需要使用 a.场景委托?
  • 我能够使用 AppDelegate 和 Storyboard(并且没有 SceneDelegate)来完成这项工作
【解决方案2】:

根据 @the.blaggy 的回答,这是我设法在 iOS 13 上运行我的项目的方法:

  1. 如果没有 SceneDelegate,请创建一个

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. 打开 info.plist 作为源代码并添加这些行:

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. 将此添加到您的 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))
            }
        }
    }

【讨论】:

    【解决方案3】:

    这可能取决于其他项目代码,但以下测试为有效(Xcode 12b),因此可能会有所帮助。

    这个想法是用可用性检查器将一个包装器隐藏在另一个结构中:

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

    【讨论】:

      【解决方案4】:

      如果你真的需要它,只需将@main 更改为@UIApplicationMain,它应该可以解决问题。

      【讨论】:

        【解决方案5】:

        结合@the.blaggy 和@Ugo Marinelli 的答案,我通过修改我的AppDelegate 使其工作,而无需创建新的SceneDelegate:

        class AppDelegate: UIResponder, UIApplicationDelegate {
            var window: UIWindow?
        
            func application(
                _: UIApplication,
                didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?
            ) -> Bool {
                let window = UIWindow(frame: UIScreen.main.bounds)
        
                window.rootViewController = UIHostingController(
                    rootView: ContentView()
                )
                self.window = window
                window.makeKeyAndVisible()
                return true
            }
        }
        

        我还像 @the.blaggy 所做的那样包装了我的主结构。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-10-13
          • 2021-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-09
          • 1970-01-01
          相关资源
          最近更新 更多