Search code examples
swiftuiswiftdata

Child view not refreshing for changes on SwiftData model


I have two models like these:

@Model
class Post {
    ...
    @Relationship(inverse: \Comment.post) var comments: [Comment]?
}

@Model
class Comment {
    ...
    var post: Post
}

And in one of my views I use a query like the one below where I show a list of posts and their comments count:

struct PostsList: View {
    @Query var posts: [Post]

    var body: some View {
        List {
            ForEach(posts) { post in
                PostListItem(post: post)
            }
        }
    }
}

struct PostListItem: View {
    @State var post: Post

    var body: some View {
        Text("\(post.title)")
        Text("\(post.comments?.count ?? 0) comments")
    }
}

When I insert a new comment with a given post, and then I go back to the view which shows the list of posts and their comments count, the count is not updated for the post I used unless I relaunch the app.

Is there a way to make SwiftData reload a given query, not when the entities returned changed, but when a related one did?


Solution

  • It turns out the issue was simply in the way I pass the single Post to PostListItem.

    Given that I am not an expert in SwiftUI, the only two way I found until now to solve this issue are the following.

    Solution 1: Remove the subview PostListItem and render each Post directly in the List.

    struct PostsList: View {
        @Query var posts: [Post]
    
        var body: some View {
            List {
                ForEach(posts) { post in
                    Text("\(post.title)")
                    Text("\(post.comments?.count ?? 0) comments")
                }
            }
        }
    }
    

    Solution 2: Wrap the Post in a view model annotated with @Observable and use it as state in PostListItem.

    struct PostsList: View {
        @Query var posts: [Post]
    
        var body: some View {
            List {
                ForEach(posts) { post in
                    PostListItem(model: post)
                }
            }
        }
    }
    
    @Observable
    class PostListItemModel {
        var post: Post
    
        init(post: Post) {
            self.post = post
        }
    }
    
    struct PostListItem: View {
        var model: PostListItemModel
    
        var body: some View {
            Text("\(model.post.title)")
            Text("\(model.post.comments?.count ?? 0) comments")
        }
    }