SwiftUI中如何使用@EnvironmentObject在AppDelegate和SceneDelegate/Views之间共享数据

Posted

技术标签:

【中文标题】SwiftUI中如何使用@EnvironmentObject在AppDelegate和SceneDelegate/Views之间共享数据【英文标题】:How to use @EnvironmentObject to share data between AppDelegate and SceneDelegate/Views in SwiftUI 【发布时间】:2019-07-17 17:27:08 【问题描述】:

在 SwiftUI 5.1 中,我想使用 AppDelegate 创建一个 userData 对象。 userData 还将包含 BLE 广告数据,这些数据也将从 AppDelegate 更新。 UI 应该可以使用这些数据来显示这些值。

我在 AppDelegate 中使用

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 

    private var centralManager : CBCentralManager!
    var userData: UserData!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        // Override point for customization after application launch.
        userData = UserData()

        return true
    

在 SceneDelegate 我想传递给视图使用

class SceneDelegate: UIResponder, UIWindowSceneDelegate 
    @EnvironmentObject var userData: UserData

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Use a UIHostingController as window root view controller
        if let windowScene = scene as? UIWindowScene 
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView:
                BeaconList().environmentObject(userData)
            )

            self.window = window
            window.makeKeyAndVisible()
        
    

编译工作正常,但运行代码时我得到

线程 1:致命错误:在外部读取 EnvironmentObject 查看.body

如果我删除了

.environmentObject(userData)

我明白了

线程 1:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)

本质上,我正在尝试从 AppDelegate 创建和更新 userData 对象,并从 SceneDelegate 显示并在下面查看。

我该如何实现?

【问题讨论】:

为什么不能直接使用SceneDelegate?这就是苹果对这类事情的要求。 (您可能发现了一个未涵盖的异常案例,但我对此表示怀疑。) @dfd 不会在每次创建新场景时创建一个 userData 的新实例吗? @PeterSchorn 你可能是对的。我的评论似乎又回到了 SwiftUI 1.0 beta 3 左右,这已经是 waaayyyy 回来了。 :-) 我现在正在开发一个 UIKit 应用程序,它仅适用于 iPad,我正在通过支持多个实例(或场景)并最终拖放。看看我上面的评论,我仍然坚持它的意图 - 在大多数情况下,如果不是全部的话,Apple 希望按场景或应用程序实例进行沙盒处理,而不是所有实例。 【参考方案1】:

也许您可以使用UIApplication.shared.delegate 让 AppDelegate 访问用户数据:

class AppDelegate: UIResponder, UIApplicationDelegate 

    var userData: UserData!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        // Override point for customization after application launch.
        userData = UserData()
        return true
    

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        let userData = (UIApplication.shared.delegate as! AppDelegate).userData

        // Use a UIHostingController as window root view controller
        if let windowScene = scene as? UIWindowScene 
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(userData))
            self.window = window
            window.makeKeyAndVisible()
        
    

【讨论】:

谢谢,我按照这种方式使用 AppDelegate,工作正常。 这是一种违反可靠原则并使 appdelegate 成为全局单例的糟糕方式。不幸的是,我没有更好的建议——苹果强迫我们的手设计很差,因为它不会给我们任何能力将依赖项正确地注入到场景代理中。

以上是关于SwiftUI中如何使用@EnvironmentObject在AppDelegate和SceneDelegate/Views之间共享数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIkit 中添加 SwiftUI 对象

如何在 SwiftUI 中使用 SFSafariViewController?

如何在 swiftUI 视图中使用按钮? [关闭]

如何在 SwiftUI 中使用 .fileimporter 保存音频文件并检索文件并在 SwiftUI 列表中显示它们的文件名?

如何使用 SwiftUI 从文本中提取 Hashtags?

如何在 Xcode Playground 中使用 SwiftUI?