像Instagram/X这样的应用在SwiftUI中如何实现跨不同信息流的帖子状态(如点赞)同步?
我在尝试理解在SwiftUI中使用新的Observation框架实现共享帖子状态的正确架构。
我有一个简单的例子,其中 ContentView 和 SavedView 都显示 PostView。
@Observable class Post {
var id: Int
var content: String
var title: String
var isLiked: Bool = false
init(id: Int, content: String, title: String, isLiked: Bool) {
self.id = id
self.content = content
self.title = title
self.isLiked = isLiked
}
func toggleLike() {
isLiked.toggle()
}
}
struct PostView: View {
var post: Post
var body: some View {
HStack {
Text(post.title)
Text(post.content)
Spacer()
Button(post.isLiked ? "Liked" : "Like") {
post.toggleLike()
}
}
}
}
ContentView
struct ContentView: View {
@State private var posts: [Post] = []
var body: some View {
List(posts, id: \.id) { post in
PostView(post: post)
}
.task {
if posts.isEmpty {
let examplePost = Post(
id: 1,
content: "examplecontent",
title: "exampletitle",
isLiked: false
)
posts.append(examplePost)
}
}
}
}
SavedView
struct SavedView: View {
@State private var posts: [Post] = []
var body: some View {
List(posts, id: \.id) { post in
PostView(post: post)
}
.task {
if posts.isEmpty {
let examplePost = Post(
id: 1,
content: "examplecontent",
title: "exampletitle",
isLiked: false
)
posts.append(examplePost)
}
}
}
}
如果我在 ContentView 点赞,SavedView 中的同一篇帖子仍然显示为未点赞。
我理解在这个示例中,每个视图都创建了自己的 Post 实例,因此它们并没有真正共享同一个对象。
我的问题更多是关于架构:
- 实际应用中通常如何解决?
- 像Instagram或 X(Twitter)这样的应用是否为每条帖子维护一个单一的真实数据源(Single Source of Truth)?
- 常见的做法是否是有一个中央存储/仓库(类似Redux/TCA),让每个屏幕通过ID引用同一个
Post对象? - 还是在用户在屏幕之间导航时重新获取数据?
- 结合SwiftUI的新Observation框架,什么样的架构算是惯用的?
我对大型社交应用是如何处理这个问题感兴趣,而不仅仅是修复这个示例。
解决方案
一些应用可能按屏幕逐个获取,一些会将数据加载到一个中央存储中,另一些则加载到一个会缓存到磁盘的中央存储中。
要将加载从基于视图迁移到基于存储,请移除 @State var posts 和 .task,改为创建一个 @Observable class PostStore,并在其初始化时加载。要让存储成为中心位置,如果你在撰写本文时使用的是Xcode 27(处于测试版),你可以使用 @State var postsStore,但如果版本较低,则需要让该类符合 ObservableObject 并使用 @StateObject,或使用fileprivate全局变量或单例。
你需要在初始化时获取帖子(将JSON或轻量级自动解码的结构转换为重量级的Post对象,消除重复项),并在从服务器接收到推送通知、定时器触发或应用生命周期通知时进行更新。然后你需要监控所有唯一的 Post 对象的变更,在一个延迟/合并的函数(或通过生命周期通知和后台任务)中将变更发送到服务器。
顺便说一句,当你的模型是中央存储中的一个对象,例如你的 Post 类时,你应该在 List 中使用 \.self 作为 id,也就是通过指针来标识它。你也可以使其符合 Identifiable 并使用 var id: ObjectIdentifier { ObjectIdentifier(self) }。