Search code examples
restswiftuimvvmios16

How to share and modify data between view models in SwiftUI?


I am making a social media app in SwiftUI and I am stuck on how to share and modify data between view models. For each view (PostListView, PostDetailView, PostUpdateView) I have a separate view model and when the user is on PostUpdateView,for example, and does changes to the post in PostUpdateViewModel, how do I reflect these changes on the PostListView?

// parent view model
class PostListViewModel: ObservableObject {
    @Published var posts = [Post]() // i want changes to any post to be reflected here 

    func fetchPosts() { ... } // fetches posts from rest api, called if self.posts is empty
}
// child view model
class PostUpdateViewModel: ObservableObject {
    @Published var post: Post // when updated, i want to see the updated post on the PostListView

    init(post: Post) { self.post = post }

    func update() { ... } // update post api call
}
struct Post: Codable, Identifiable {
   var id: Int
   var title: String
   var content: String
   // other properties
}

So far I've seen people just refetch the data whenever they go back to the view containing the list of data, but that solution doesn't really work for me as my data is paginated, and when refetching, it just fetches the first page and sends the user back to the top of their feed. Two possible solutions I've thought of are either having one shared view model between all the views, regarding posts, or instead of storing the posts themselves in the parent view model, to store a list of child view models.

Thanks!


Solution

  • Usually when I need to share data between different views I put that data in a data store or aggregate model. This is shown below:

    class CatalogStore: ObservableObject {
        
        private var storeHTTPClient: StoreHTTPClient
        
        init(storeHTTPClient: StoreHTTPClient) {
            self.storeHTTPClient = storeHTTPClient
        }
        
        @Published var products: [Product] = []
        @Published var categories: [Category] = []
        
        func addProduct(_ product: Product) async throws {
             try await storeHTTPClient.addProduct(product)
        }
        
        func populateProducts() async throws {
            self.products = try await storeHTTPClient.loadProducts()
        }
    }
    

    Now, I can inject an instance of CatalogStore into the enviromentObject and access it from all the views.