Why is it `_model` in the init of AppRoot?
sebastienkb opened this issue · 4 comments
The init of the app occurs as follows
init() {
_model = StateObject(wrappedValue: AppModel())
}
When I set as model, the compiler complains with Cannot assign to property: 'model' is a get-only property
For which reason does adding an underscore in front of the var fix this particular case issue? What are we telling the compiler?
It's by searching the solution on google that I stumbled on this post but I don't understand the implications, nor why it isn't possible in other (more verbose) code.
You can inject @StateObject during initialization by unwrapping the property with
_
Is this in the official Apple documentation? How did you find this solution?
Hi @sebastienkb,
The @StateObject property wrapper, and method that it calls StateObject(wrappedValue:) , like the other SwitftUI @ ...` magic wrappers, under the hood create a structure that gives access to the actual wrapped value and a Combine Publisher for the wrapped value.
The _model is the variable that points at the space that the compiler creates to store that structure in and what has to be initialised in the init method.
I figured it out when I was trying to implement Protocols for the MVVM [0] pattern's @ObservableObjects and their @Published vars. See Senpai's post for info on this, does a far better job of explaining what's going on than me.
Apple documentation, not that I can recall. But the "_" is in many languages is something that is by convention private i.e. you can as a developer use it if you know what you're doing - so if there's now an "official" alternative way to achieve the same result I would advise using it.
Kind regards
Jon
[0] I spent way too much getting MVVM working with protocols and all the rest :-/. As a learning experience it was great, but fwiw none of my current projects use a MVVM type pattern. The problem I have with it is it doesn't feel like it's got Apple blessing and as such adopting it involved re-inventing far too much of the @ magic that SwiftUI gives for free. Anyway, that's my two cents ...
I agree with forcing the "classic" MVVM pattern and I'd like to avoid it, but sooner or later I can't help but needing some companion object that would call an api and update the context with the retrieved data among other things related to the view. I'm considering splitting the body part in one file and all other functions in another, to have less content and make browsing easier.
The apis still need the Set<AnyCancellable> so I'd need a var that preferably isn't a @State, so that has goes outside of the View. How would you get around that?
Then goes the issue of saving in Core Data with the viewContext that I keep dragging around everywhere because having it as a singleton seems to be such a bad idea (Apple docs and SO answers). Your protect seemed to fix this, but magic always comes with a price...
Hi @sebastienkb,
For sure on the companion object :-)
fwiw with SwiftUI after many mistakes/learning experiences I've settled on:
-
A
ObservableObjectbased AppModel that fulfils the companion role of handling everything that the@SwiftUI properties do not, e.g. for CoreData pretty much everything except fetching the records. It's a singleton and gets setup as a@StateObjectin@mainand is then injected into all of the Smart Views. -
Then the View's get split into:
- Pure stateless Layout Views.
- Smart, state injector Views that wraparound the Layout Views and provide it with their data via
@properties and theAppModelas well as callback intents, bindings etc to facillitate manipulating the AppModel. (a bit view model ish ...)
Only real downside to the approach is I can't mock the Smart Views in the way that I could with a View Model, but practically not really an issue because of the pure Layout view. Other than that seems to scale okay, maintains a reasonable separation of concerns, doesn't involve much extra code and the AppModel is mockable.
Do still wish Apple would make their ObservableObject handle more of the @ goodness, as the full VM approach is conceptually more elegant ...
Good luck with your projects.
Hi @sebastienkb ,
Came across post on the excellent Swiftui-lab website that the proprietor took from one of the wwdc 21's digital lounge. Confirms that using the _model = StateObject(wrappedValue:) and similiar is fine (and they need to get their docs updated). Post can be found over here .
Kind regards
Jon
The init of the app occurs as follows
init() { _model = StateObject(wrappedValue: AppModel()) }When I set as
model, the compiler complains withCannot assign to property: 'model' is a get-only propertyFor which reason does adding an underscore in front of the var fix this particular case issue? What are we telling the compiler?
It's by searching the solution on google that I stumbled on this post but I don't understand the implications, nor why it isn't possible in other (more verbose) code.
You can inject @StateObject during initialization by unwrapping the property with
_Is this in the official Apple documentation? How did you find this solution?