Swift Firebase 自定义对象存储问题:如何将UnlockingCharacters结构体持久化到Firestore用户文档
解决Firebase自定义结构体持久化与类型错误问题
看起来你现在卡了几个关键问题:解锁的角色没法持久化到Firebase、结构体编码出错,还有初始化时的类型不匹配问题。我一步步帮你梳理解决:
1. 修正结构体的Codable兼容性与结构优化
首先,你的UnlockingCharacters其实可以简化——它只包裹了一个Character,没必要多一层嵌套,直接在UserModel里存[Character]会更清爽(如果坚持原结构也可以,后面会适配)。另外要确保所有结构体的Codable实现正确,尤其是@DocumentID的使用:
struct Character: Identifiable, Codable { @DocumentID var id: String? var character_name: String var character_type: String var character_image: String var character_details: String var character_usersUnlocking: Int var character_totalPoints: Int // 去掉user字段避免循环引用(UserModel里也包含Character,会导致编码失败) var didUnlock: Bool? = false var isUnlocking: Bool = false // 可选:手动指定编码键,避免Firestore字段名混乱 enum CodingKeys: String, CodingKey { case id case character_name case character_type case character_image case character_details case character_usersUnlocking case character_totalPoints case didUnlock case isUnlocking } } // 如果坚持保留UnlockingCharacters,确保Codable正确: struct UnlockingCharacters: Identifiable, Codable { var id = UUID().uuidString var character: Character enum CodingKeys: String, CodingKey { case id case character } } struct UserModel: Identifiable, Codable { var username : String var pic : String var bio: String var uid : String var id: String { uid } var activeUnlockingCharacters: [UnlockingCharacters] // 新增便捷构造器,解决ViewModel初始化错误 init(username: String = "", pic: String = "", bio: String = "", uid: String = "", activeUnlockingCharacters: [UnlockingCharacters] = []) { self.username = username self.pic = pic self.bio = bio self.uid = uid self.activeUnlockingCharacters = activeUnlockingCharacters } }
2. 修正ProfileViewModel的初始化错误
你之前把类型[SupportingCharities]直接传给参数,这是错误的,应该传空数组实例:
// 错误写法: // @Published var userInfo = UserModel(username: "", pic: "", bio: "", uid: "", activeSupportingCharities: [SupportingCharities]) // 修正后(用便捷构造器自动填充默认值): @Published var userInfo = UserModel() // 或者手动传空数组: @Published var userInfo = UserModel(username: "", pic: "", bio: "", uid: "", activeUnlockingCharacters: [])
3. 修复fetchUser函数的Firestore读写错误
你的fetchUser有两个严重问题:一是调用setData(from: UnlockingCharacters)时传的是类型而非实例;二是completion里传的也是类型而非数组实例。正确逻辑应该是读取用户数据,写入操作要放在角色解锁/移除的函数里:
let ref = Firestore.firestore() // 假设你有全局或可访问的SharedDataModel实例 let sharedData = SharedDataModel() func fetchUser(uid: String, completion: @escaping (UserModel) -> ()){ ref.collection("Users").document(uid).getDocument { (doc, err) in if let err = err { print("获取用户数据失败:\(err)") return } guard let doc = doc, doc.exists else { print("用户文档不存在") return } do { // 用Firestore自带的Codable解析UserModel let user = try doc.data(as: UserModel.self) DispatchQueue.main.async { completion(user) // 同步到SharedDataModel,让UI显示已解锁的角色 sharedData.unlockingCharacters = user.activeUnlockingCharacters.map { $0.character } } } catch let error { print("解析用户数据失败:\(error)") } } }
4. 实现角色解锁/移除的持久化逻辑
用户点击按钮时,不仅要更新SharedDataModel,还要同步更新UserModel并保存到Firebase:
// 假设你在ViewModel里有当前用户的实例 @Published var currentUser: UserModel? func addToUnlocking(characterData: Character) { guard var currentUser = currentUser else { return } if let index = sharedData.unlockingCharacters.firstIndex(where: { $0.id == characterData.id }) { // 移除角色 sharedData.unlockingCharacters.remove(at: index) currentUser.activeUnlockingCharacters.removeAll { $0.character.id == characterData.id } } else { // 添加角色 sharedData.unlockingCharacters.append(characterData) let unlockingChar = UnlockingCharacters(character: characterData) currentUser.activeUnlockingCharacters.append(unlockingChar) } // 保存更新后的用户数据到Firebase saveUserToFirebase(user: currentUser) } func saveUserToFirebase(user: UserModel) { ref.collection("Users").document(user.uid).setData(from: user) { error in if let error = error { print("保存用户数据失败:\(error)") } else { print("解锁角色已成功持久化") } } }
5. 关键注意事项
- 循环引用规避:
Character里的user字段会和UserModel形成循环引用,导致Codable编码失败,必须移除或用@Ignored标记(如果不需要存储到Firebase)。 - Firestore权限配置:确保你的Firestore规则允许用户读写自己的文档,示例规则:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /Users/{uid} { allow read, write: if request.auth != null && request.auth.uid == uid; } } } - UUID稳定性:
UnlockingCharacters的id用UUID().uuidString每次初始化都会生成新ID,若需要追踪同一解锁记录,可调整为基于Character.id生成。
按照上面的步骤修改后,解锁的角色就能正确持久化到Firebase,重启APP后也能从Firestore读取并显示了。
内容的提问来源于stack exchange,提问作者Mxyb
相关产品推荐
相关产品推荐

