Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use @EnvironmentObject to share data between AppDelegate and SceneDelegate/Views in SwiftUI

In SwiftUI 5.1, I want to use AppDelegate to create an userData object. userData will also contain BLE advertisement data, which will be updated from AppDelegate, too. These data should be available to the UI to display those values.

In AppDelegate I use

@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
    }

In SceneDelegate I want to pass to the view using

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()
        }
    }

Compiling works fine, but when running the code I get

Thread 1: Fatal error: Reading EnvironmentObject outside View.body

If I remove the

.environmentObject(userData)

I get

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

Essentially, I'm trying to make create and update the userData object from AppDelegate, and to display from SceneDelegate and view below.

How can I implement this?

like image 292
AnErd Avatar asked Jul 17 '19 17:07

AnErd


People also ask

What is the difference between an @environment property and an @EnvironmentObject property?

SwiftUI gives us both @Environment and @EnvironmentObject property wrappers, but they are subtly different: whereas @EnvironmentObject allows us to inject arbitrary values into the environment, @Environment is specifically there to work with SwiftUI's own pre-defined keys.

Can you have multiple environment objects SwiftUI?

Using multiple environment objectsYou can define as many environment objects as you like. The principle works precisely the same, as long as you provide an environment object through the view modifier for each requested environment object type.

Does SwiftUI have Appdelegate?

New in iOS 14SwiftUI is responsible for creating that delegate and looking after its lifetime, so you can go ahead and add any other app delegate functionality to that class to have it called.


1 Answers

Maybe you can use the UIApplication.shared.delegate to get the AppDelegate an access the user data:

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()
        }
    }
like image 98
rraphael Avatar answered Oct 16 '22 09:10

rraphael