Swift结构体新增属性后Firebase旧数据无法加载,如何设置可选属性?
如何让Firebase实时数据库兼容结构体新增的可选属性?
你说得没错,旧数据加载失败的核心原因就是新增的属性在快照解析时被当成了必填项,而旧数据里没有这些字段,导致init?(snapshot:)初始化器直接返回nil,所以旧条目加载不出来。下面我帮你一步步修改代码,让结构体兼容新旧数据:
问题分析
你的InsuranceItem里description和userId已经声明为可选类型,但在快照解析的guard语句里,你还是把它们当成必选值去强制绑定了——旧数据没有这两个字段,所以guard条件不满足,初始化失败。另外toAnyObject()里用了强制解包description!和userId!,如果这两个属性是nil的话会直接崩溃,也需要修复。
解决方案
1. 修改快照初始化器,允许可选属性为nil
把guard语句里的可选属性判断改成可选绑定(允许nil),也就是去掉guard里的强制要求,改成直接从字典里取,没有的话就设为nil:
init?(snapshot: DataSnapshot) { guard let value = snapshot.value as? [String: AnyObject], let name = value["name"] as? String, let timestamp = value["timestamp"] as? Int, let itemValue = value["itemValue"] as? Int else { // 核心必填项缺失才返回nil,可选字段不影响初始化 return nil } // 可选字段单独处理,没有的话自动为nil let description = value["description"] as? String let userId = value["userId"] as? String self.ref = snapshot.ref self.key = snapshot.key self.name = name self.timestamp = timestamp self.itemValue = itemValue self.description = description self.userId = userId }
2. 调整默认初始化器的参数
原来的初始化器里description默认传空字符串,userId是必填项,我们可以改成让description默认nil,userId也设为可选参数(如果需要的话):
init(name: String, timestamp: Int, itemValue: Int = 0, description: String? = nil, userId: String?, key: String = "") { self.ref = nil self.key = key self.name = name // 注意:你原来这里写的是Int(Date().timeIntervalSince1970),覆盖了传入的timestamp参数,这是bug! self.timestamp = timestamp self.itemValue = itemValue self.description = description self.userId = userId }
这里顺便帮你修复一个小bug:原来的初始化器里你忽略了传入的
timestamp参数,直接用了当前时间,现在改成使用传入的值,符合初始化器的设计意图。
3. 修复toAnyObject()的强制解包问题
不要用!强制解包可选属性,而是用可选绑定或者空合运算符,确保只有当属性有值时才加入字典:
func toAnyObject() -> Any { var dict: [String: Any] = [ "name": name, "timestamp": timestamp, "itemValue": itemValue ] // 可选属性有值才加入字典 if let description = description { dict["description"] = description } if let userId = userId { dict["userId"] = userId } return dict }
修改后的完整代码
import Foundation import Firebase struct InsuranceItem { let ref: DatabaseReference? let key: String let name: String let timestamp: Int let itemValue: Int let description: String? let userId: String? init(name: String, timestamp: Int, itemValue: Int = 0, description: String? = nil, userId: String?, key: String = "") { self.ref = nil self.key = key self.name = name self.timestamp = timestamp self.itemValue = itemValue self.description = description self.userId = userId } init?(snapshot: DataSnapshot) { guard let value = snapshot.value as? [String: AnyObject], let name = value["name"] as? String, let timestamp = value["timestamp"] as? Int, let itemValue = itemValue as? Int else { return nil } let description = value["description"] as? String let userId = value["userId"] as? String self.ref = snapshot.ref self.key = snapshot.key self.name = name self.timestamp = timestamp self.itemValue = itemValue self.description = description self.userId = userId } func toAnyObject() -> Any { var dict: [String: Any] = [ "name": name, "timestamp": timestamp, "itemValue": itemValue ] if let description = description { dict["description"] = description } if let userId = userId { dict["userId"] = userId } return dict } }
这样修改后,旧数据因为没有description和userId字段,这两个属性会被设为nil,初始化器可以正常完成,旧条目就能加载出来了;新数据的可选属性也能正常保存到数据库里。
内容的提问来源于stack exchange,提问作者rebellion
相关产品推荐
相关产品推荐

