SwiftUI 应用在支持 iOS 13 时不 运行

SwiftUI app doesn't run when supporting iOS 13

应用程序在部署目标为 15.2 时运行良好,但后来我需要将部署目标更改为 iOS 13 以支持更多设备。在 iOS 14+ 上仍然可以正常工作,但是当它在模拟器上运行在 iOS 13 上时会崩溃并出现异常 Thread 1: signal SIGABRT。控制台没有显示调试信息:

CoreSimulator 802.6 - Device: iPhone 11 (iOS 13.0) (6CCC2B89-002B-4E83-8175-C244984BFC46) - Runtime: iOS 13.0 (17A577) - DeviceType: iPhone 11
Message from debugger: Terminated due to signal 9

以下是 WeatherApp.swift 文件代码的相关部分以及错误的屏幕截图:

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

struct WeatherApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var loginStatus = UserLoginStatus()
    
    @available(iOS 14.0, *)
    var body: some Scene {
        WindowGroup {
            NavigationView{
                if Methods.UserAccount.getFromUserDefaults()["username"] != nil {
                    MainView(loggedIn: $loginStatus.loggedIn)
                    LoginView(username: "", password: "", loggedIn: $loginStatus.loggedIn)
                } else {
                    LoginView(username: "", password: "", loggedIn: $loginStatus.loggedIn)
                    MainView(loggedIn: $loginStatus.loggedIn)
                }
            }
        }
    }

    class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

            //Some app-specific setup code
            ...
            
            return true
        }
        
    }
}

class UserLoginStatus: ObservableObject {
    @Published var loggedIn: Bool {
        didSet {
            print("The value of loggedIn changed from \(oldValue) to \(loggedIn)")
        }
    }
    
    init () {
        loggedIn = false
    }
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    @StateObject var loginStatus = UserLoginStatus()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let mainView = MainView(loggedIn: $loginStatus.loggedIn)
        let loginView = LoginView(username: "", password: "", loggedIn: $loginStatus.loggedIn)

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            
            if Methods.UserAccount.getFromUserDefaults()["username"] != nil {
                window.rootViewController = UIHostingController(rootView: mainView)
            } else {
                window.rootViewController = UIHostingController(rootView: loginView)
            }
            
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    
}

另外,这里是Info.plist文件的截图,具体展开相关区域:

根据 Apple docsStateObject 仅适用于 iOS 14+。

StateObject 可从 SwiftUI 2.0 (iOS 14) 获得,旨在在视图中工作,而不是在 class 中工作(如 SceneDelegate

对于提供的场景,一个可能的解决方案是在顶层(在委托或主视图中)仅将 logicState 用作 属性,但将其完全注入子视图(而不是仅仅绑定)并在内部进行观察。

所以它应该看起来像

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var loginStatus = UserLoginStatus() // << just hold here

// ...

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let mainView = MainView(loginStatus: loginStatus) // << here !!


struct MainView: View {
    @ObservedObject var loginStatus: UserLoginStatus   // << injected here

// ...
}

类似的可以在WeatherApp

中完成