Swift中Firestore@DocumentID覆盖嵌套类型同属性的解决方案咨询
解决Firestore嵌套Codable类型@DocumentID被根文档ID覆盖的问题
当使用Firestore的setData(from:)处理包含嵌套Codable类型(如Book包含Tag,且Tag带有@DocumentID)的对象时,嵌套类型的@DocumentID会被根类型的文档ID覆盖,这是官方预期行为。除了创建不含@DocumentID的子类型外,以下是几种可行的解决方案:
方案1:自定义编码/解码逻辑,手动处理嵌套类型的ID字段
直接为嵌套类型(如Tag)实现自定义的Codable逻辑,绕过Firestore对@DocumentID的默认处理,将ID作为普通字段存储和读取。同时保留单独操作嵌套类型时的@DocumentID适配能力。
示例代码:
import FirebaseFirestoreSwift struct Tag: Codable { // 用普通属性存储ID,避免Firestore默认逻辑干扰 var tagId: String? var name: String // 单独存储/读取Tag时的初始化方法,适配@DocumentID逻辑 init(id: String?, name: String) { self.tagId = id self.name = name } init?(document: DocumentSnapshot) { guard let data = document.data(), let name = data["name"] as? String else { return nil } self.tagId = document.documentID self.name = name } } struct Book: Codable { @DocumentID var id: String? var title: String var tags: [Tag] } // 写入Book时,Tag的tagId会被正常存储,不会被Book的id覆盖 let book = Book(id: "book123", title: "Swift入门", tags: [Tag(id: "tag456", name: "编程")]) try? db.collection("books").document(book.id!).setData(from: book)
方案2:自定义属性包装器替代@DocumentID用于嵌套类型
创建一个自定义属性包装器,专门用于嵌套类型的ID存储,让Firestore的默认编码器不会将其识别为文档ID字段,从根源避免覆盖问题。
示例代码:
import FirebaseFirestoreSwift @propertyWrapper struct NestedDocumentID: Codable { var wrappedValue: String? init(wrappedValue: String?) { self.wrappedValue = wrappedValue } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(wrappedValue) } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() wrappedValue = try container.decode(String?.self) } } struct Tag: Codable { @NestedDocumentID var id: String? var name: String } struct Book: Codable { @DocumentID var id: String? var title: String var tags: [Tag] } // 写入时,Tag的@NestedDocumentID会被作为普通字段存储,不会被Book的ID覆盖 let tag = Tag(id: "tag456", name: "编程") let book = Book(id: "book123", title: "Swift入门", tags: [tag]) try? db.collection("books").document(book.id!).setData(from: book)
方案3:写入前临时备份嵌套类型ID,写入后恢复
这是一个临时的应急方案,适合快速解决问题。在调用setData(from:)之前备份嵌套类型的ID,写入完成后再恢复原值。
示例代码:
import FirebaseFirestoreSwift struct Tag: Codable { @DocumentID var id: String? var name: String } struct Book: Codable { @DocumentID var id: String? var title: String var tags: [Tag] } var book = Book(id: "book123", title: "Swift入门", tags: [Tag(id: "tag456", name: "编程")]) // 备份所有Tag的ID let tagIds = book.tags.map { $0.id } // 写入Firestore try? db.collection("books").document(book.id!).setData(from: book) // 恢复Tag的ID for (index, id) in tagIds.enumerated() { book.tags[index].id = id }
注意事项
- 方案1和方案2更推荐,它们从编码逻辑层面解决问题,避免后续维护时的潜在风险。
- 如果需要单独存储和编辑
Tag,可以在单独操作Tag时使用@DocumentID,嵌套到Book中时用上述方案处理ID字段。
内容的提问来源于stack exchange,提问作者Paul B
相关产品推荐
相关产品推荐

