Let's say I'm building app for viewing and editing posts. Here's my model for Post and app itself:
struct Post: Hashable {
var id: Int
var title: String
}
class Store: ObservableObject {
//
// In real-life this should be fetched via API on app/view start
//
@Published var items: [Post] = [
Post(id: 1, title: "Foo"),
Post(id: 2, title: "Bar"),
Post(id: 3, title: "baz"),
]
}
@main
struct PlaygoundApp: App {
@StateObject var store = Store()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(store)
}
}
}
And here's the list with all posts. I can navigate to each post's own view and edit post data (usually via API):
struct ContentView: View {
@EnvironmentObject var store: Store
var body: some View {
NavigationView {
List {
ForEach(store.items, id: \.self) { post in
NavigationLink(destination: PostView(post: post).navigationTitle(post.title)) {
Text(post.title)
}
}
}
}
}
}
struct PostView: View {
var post: Post
var body: some View {
Text(post.title)
Button(action: {
//
// Make API call and edit Post title
/// THIS WILL NOT COMPILE
// post.title = "New title"
//
}, label: {
Text("Edit title")
})
}
}
So if I try to edit post (directly or after API call, it doen't matter), I:
- can't assign new value to existing
var post: Post
struct from post itself
- cant trigger view to be re-rendered
What is the correnct approach to reflect data changes in existing Observable storage? I thought of two ways to fix it:
- make method like
store.fetch_posts()
and call it after pressing button, but this will trigger to re-render all post view and reset scrolling position, and likely it will not work at all as expected
- in Ios15 there's abillity to iterate over bindings (https://www.swiftbysundell.com/articles/bindable-swiftui-list-elements/). I thought this might be a solution, but there should be existing way to handle such a generic usecase
Thank you