0

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)?

Tomáš Kafka
  • 4,405
  • 6
  • 39
  • 52

1 Answers1

1

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.

lorem ipsum
  • 21,175
  • 5
  • 24
  • 48
  • Thank you, that was it - I should have provided a default value. As for `@EnvironmentObject` vs `@ObservedObject`, a great description of differences is here https://stackoverflow.com/q/63343819/38729 - so far I dislike having 'invisible magical objects' that transport state across the hieararchy :). – Tomáš Kafka Apr 02 '21 at 12:14