@Polyphonic This is great stuff Polyphonic, thank you! Data Model vs View Model makes a lot of sense in particular. Do you think, conceptually, this could be seen as the "SwiftUI world" (aka what is handled by the View Model), and the "non-SwiftUI world" (aka the Data Model)? To me it seemed like that causes 2 sources of truth, like we have now...but maybe that is normal? As in, the View Model only manages the data as long as the view (the UI world) is alive, but once it disappears and is done, then its data must be transferred over to the Data Model?
If that's the case, it still makes me wonder how a plain vanilla class like DoNetworkStuff has access to that data. I see what you're saying that starting the app with an AppViewModel as an .environmentObject is the way to persist the whole app's data in a single, non-replicated class, but it still seems like SwiftUI isn't really designed for passing that data to plain classes. To quote this StackOverflow comment, https://stackoverflow.com/a/65370757/1359088,
A possible approach is to make it shared (and don't use @EnvironmentObject anywhere outside SwiftUI view - it is not designed for that)
I found it interesting that he/she said the @EnvironmentObject isn't designed for being used outside the view. His/her answer is the same as your suggested solution, which is to create a shared instance of the ObservableObject, which serves both worlds:
In the SwiftUI world, it uses its @Published vars to publish updates to views that need to display the appViewModel's values as they change;
In the non-SwiftUI world, it makes itself available via the appViewModel.shared instance, which is where DoNetworkStuff should go to fetch the devId.
Why is it that plain classes like DoNetworkStuff can't see an environmentObject? Why wasn't that made a feature? Why must you "bridge the gap" between the SwiftUI ObservableObject and this plain class by making the shared instance?
Or, maybe you could make all classes like DoNetworkStuff "opt-in" to the SwiftUI world by importing SwiftUI and making them conform to the ObservableObject protocol--in which case they WOULD be able to see the environmentObject? Do you think that's a better solution, is to make all classes operate within the SwiftUI world, or is it better to leave them as plain classes and bring the data over to them via the shared instance?