Tomáš Kafka
Tomáš Kafka

Reputation: 4833

How to add @StateObject ViewModel to an App (only one state object to be shared for all windows of app)?

In order to have my iOS/iPadOS app working well with multiple windows, I'd like to divide app state into 'app state' (eg. data layer, state of in app purchases etc.) and 'window state' (with UI state variables specific for a single window, like isDetailViewOpen).

Here is how I imagined it:

App.swift:

import SwiftUI

@main
struct WeathergraphApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var appState: AppState

    var body: some Scene {
        WindowGroup {
            ContentView(appState: appState)
        }
    }
}

ContentView.swift:

import SwiftUI

struct ContentView: View {
    @StateObject var windowState = WindowState()
    @ObservedObject var appState: AppState

    var body: some View {
        ZStack(alignment: Alignment.top) {
            MyViewView(appState: appState, windowState: windowState)
        }
        .sheet(isPresented: $windowState.purchasesIsPresented) {
            PurchasesView(appState: appState)
        } // I am referring to windowState here, so that one window can have purchases popup open, while other won't
    }
}

However, Swift won't compile my App.swift with an error Protocol requires initializer 'init()' with type '()'.

Any idea how to have a shared app state without resorting to singletons (or @Environment, which seems same to me)?

Upvotes: 0

Views: 982

Answers (1)

lorem ipsum
lorem ipsum

Reputation: 29251

The issue is likely here

@StateObject var appState: AppState

It should be something like

@StateObject var appState: AppState = AppState()

It can't come from another View or the AppDelegate. @StateObject is a property wrapper type that instantiates an observable object.

Then pass it along with

@EnvironmentObject var appState: AppState

not an @ObservedObject

https://developer.apple.com/documentation/swiftui/stateobject

If you initialization is coming from the AppDelegate you will likely solve your error by switching @StateObject var appState: AppState to @ObservedObject var appState: AppState and then continue passing it along with @EnvironmentObject.

Just as a word of caution from my experience and other experiences I've read in SO the Observable Object property wrappers only seem to work effectively for 2 or 3 layers any deeper and you start getting inconsistencies.

Upvotes: 2

Related Questions