SwiftUI中如何为Post模型创建随数据更新的commentIds变量?
我有如下格式的JSON数据:
{ "id": 1, "comments": [ {"id": 1, "message": "Hello"}, {"id": 2, "message": "There"}, {"id": 3, "message": "World"} ] }
针对该JSON,我定义了Post和Comment模型:
struct Post: Codable, Identifiable { let id: Int let comments: [Comment] enum CodingKeys: String, CodingKey { case id = "id" case comments = "comments" } } struct Comment: Codable, Identifiable { let id: Int let message: String enum CodingKeys: String, CodingKey { case id = "id" case message = "message" } }
在SwiftUI中,我使用EnvironmentObject确保数据变化时视图自动更新,这一功能运行正常。但我希望在Post模型中新增一个变量commentIds,用于存储所有Comment的id数组(示例中应为[1, 2, 3])。已知计算属性无法在SwiftUI数据更新时自动触发视图更新,请问如何创建可随Post数据更新且同步更新视图的自定义变量?
提供两种适配SwiftUI响应式更新的实现方式,可根据模型使用场景选择:
方式一:将Post改为ObservableObject(推荐用于频繁修改的场景)
把Post从struct改为遵循ObservableObject的class,用@Published标记comments属性,同时把commentIds作为存储属性,在comments的didSet回调中同步更新。这样当comments变化时,既会触发SwiftUI视图更新,也能保证commentIds始终与comments的id数组一致:
class Post: ObservableObject, Codable, Identifiable { let id: Int @Published var comments: [Comment] { didSet { commentIds = comments.map { $0.id } } } var commentIds: [Int] // 手动实现Codable解码逻辑,初始化时同步生成commentIds init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) comments = try container.decode([Comment].self, forKey: .comments) commentIds = comments.map { $0.id } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(comments, forKey: .comments) } enum CodingKeys: String, CodingKey { case id, comments } // 自定义初始化方法,用于手动创建Post实例 init(id: Int, comments: [Comment]) { self.id = id self.comments = comments self.commentIds = comments.map { $0.id } } } struct Comment: Codable, Identifiable { let id: Int let message: String }
使用时,将Post实例包装为@StateObject或注入为EnvironmentObject,当comments修改时,commentIds会自动更新并触发视图刷新。
方式二:保持Post为struct(适用于数据修改较少的场景)
如果希望保留Post值类型的特性,可将commentIds设为存储属性,在初始化(包括解码)时赋值,并提供一个mutating方法来同步修改comments和commentIds:
struct Post: Codable, Identifiable { let id: Int var comments: [Comment] var commentIds: [Int] enum CodingKeys: String, CodingKey { case id, comments } // 解码时自动生成commentIds init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) comments = try container.decode([Comment].self, forKey: .comments) commentIds = comments.map { $0.id } } // 自定义初始化方法 init(id: Int, comments: [Comment]) { self.id = id self.comments = comments self.commentIds = comments.map { $0.id } } // 同步更新comments和commentIds的方法 mutating func updateComments(_ newComments: [Comment]) { comments = newComments commentIds = newComments.map { $0.id } } } struct Comment: Codable, Identifiable { let id: Int let message: String }
这种方式下,必须通过调用updateComments方法修改comments,不能直接给comments赋值,才能保证commentIds同步更新。当Post实例作为@State或包装在ObservableObject(如@Published var post: Post)中注入为EnvironmentObject时,修改后会触发视图更新。
内容的提问来源于stack exchange,提问作者user24359450

