So I'm building a relatively simple app using all the latest iOS 17 SwiftUI and Swift Data. I'm trying to follow Apple sample code and the "Model View" pattern (which I find pretty helpful Building Large Scale Apps Swiftui | AzamSharp )
It's not clear to me what is the right way to share data between child views, as they don't update as I would expect. I would think that using an object with @Model
which of course also gives it @Observable
that putting more logic in those to update Views would be the elegant solution, but the Views don't always update as I would expect. (like if you had a @State
and a @Binding
)
So below is my example code, and here are some questions I have that I haven't found clear answers to. (I apologize this is quite long, but I wasn't sure how else to ask, I'm looking for deeper understanding not just a "make it work" but rather learning best practices.)
-
when I pass my
@Model
object, in this caseJacket
from one view to another, is it creating a copy or is it a shared reference? I thought since its a class its shared, so if I make a change on one it affects the other, however my views don't seem to behave that way. e.g. what is my source of truth? -
is this the right way to share logic between views, just using the
@Model
object for it all? I thought since it had@Observable
this would be the most elegant solution. However, maybe I should create a@State
or@StateObject
separately (a viewModel, coordinator etc.) and take that logic out of myJacket @Model
? -
If I mark
Jacket
@Bindable
to toggle state (like a modal), it does not happen right away, only when the view is dismissed and reloaded, is this a problem with needing the main thread and@MainActor
or something else? -
In my content view, the detail view
Jacket
is coming from alet
from the selection ID on the array of Jackets. (this is how some Apple code demonstrated it with SwiftData Queries) However, am I losing the ability for them to share state this way? Apple elsewhere recommended selection being the type of the object itself. And in that case, should it then be a@Bindable var
even if I don't have bindings but want children to update parents and vice versa?
import SwiftUI
import SwiftData
struct Sample: View {
@Query private var jackets: [Jacket]
@Environment(NavigationModel.self) private var navModel
var body: some View {
NavigationSplitView {
@Bindable var navModel = navModel
List(jackets, selection: $navModel.selectedId) { jacket in
JacketRow(jacket: jacket)
}
} detail: {
if let jacket = jackets[navModel.selectedId] {
JacketDetail(jacket: jacket)
} else {
ContentUnavailableView("Nothing selected", systemImage: "filemenu.and.selection")
}
}
}
}
struct JacketDetail: View {
var jacket: Jacket
var body: some View {
VStack {
Text(jacket.title)
.font(.title)
JacketChildDetail(jacket: jacket)
.padding()
}
}
}
struct JacketChildDetail: View {
var jacket: Jacket
var body: some View {
if jacket.frontImageData {
// some view
} else {
// some other view
}
}
}