我正在 SwiftUI 中制作一个社交媒体应用程序,但我一直困惑于如何在视图模型之间共享和修改数据。对于每个视图(PostListView、PostDetailView、PostUpdateView),我都有一个单独的视图模型,例如,当用户位于 PostUpdateView 上并对 PostUpdateViewModel 中的帖子进行更改时,如何在 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
}
到目前为止,我看到人们只要返回到包含数据列表的视图就重新获取数据,但是该解决方案对我来说并不真正有效,因为我的数据是分页的,并且在重新获取时,它只是获取第一个页面并将用户带回其提要的顶部。我想到的两种可能的解决方案是,关于帖子,在所有视图之间使用一个共享视图模型,或者不将帖子本身存储在父视图模型中,而是存储子视图模型的列表。谢谢!
class PostUpdateView : View {
let postID: UUID
@State var post: Post?
var body: some View {
Text(post.title ?? "Loading...")
.task(id: postID){
if postID == post?.id { return } // prevent re-downloading on re-appear
post = await fetchPost(id: postID)
}
}
}
最好尽可能避免在 Swift/SwiftUI 中使用类,因为它会导致一致性错误,任务会为你管理一个对象并具有正确的行为,在出现时初始化,在消失时取消和取消初始化等,而你自己很难做到这一点.
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()
}
}
现在,我可以将 CatalogStore 的实例注入到 enviromentObject 中并从所有视图访问它。