Xcode中Decodable扩展未被调用问题求助:IGNotification数组字典解码失败
我来帮你拆解下目前遇到的问题——你的compactMap没产出预期的IGNotification实例,大概率是自定义的Decodable扩展初始化器在某个环节失败了,但因为你用了try?吞掉了错误,所以看不到具体原因。下面是几个最可能的问题点和对应的修复方案:
1. Firestore字典包含JSONSerialization不支持的类型
Firestore返回的$0.data()字典里,经常会有Timestamp、GeoPoint这类非JSON原生类型,而JSONSerialization.data(withJSONObject:)只能处理字符串、数字、数组、普通字典和nil。一旦遇到这些特殊类型,这个方法就会抛出错误,而你用了try?,直接返回nil,导致初始化器提前退出,看起来就像扩展没被调用。
修复方案:改用Firestore自带的解码器
Firestore专门提供了Firestore.Decoder,可以直接处理Timestamp、GeoPoint这类类型,不需要先转成JSON Data。修改你的Decodable扩展:
extension Decodable { /// Create model with Firestore dictionary /// - Parameter dictionary: Firestore data init?(with dictionary: [String: Any]) { let decoder = Firestore.Decoder() do { self = try decoder.decode(Self.self, from: dictionary) } catch { print("解码失败:\(error)") // 打印错误日志,方便排查 return nil } } }
这样就跳过了JSON序列化的步骤,直接用Firestore的解码器处理原生数据类型。
2. IGNotification的Decodable实现存在问题
如果你的IGNotification模型的属性类型和Firestore里的字段不匹配,或者CodingKeys设置错误,即使转成了JSON Data,JSONDecoder也会解码失败,同样返回nil。
排查要点:
- 确保模型里的属性类型和Firestore字段一致:比如Firestore里存的是Timestamp,模型里可以用
Date(Firestore.Decoder会自动转换)或者Timestamp类型;如果是数字,模型里不要用字符串类型。 - 检查自定义CodingKeys:如果用了CodingKeys,要保证key值和Firestore的字段名完全匹配(大小写敏感)。
3. 错误被静默吞掉,无法定位问题
你原来的扩展里用了两个try?,任何一步失败都会直接返回nil,没有任何错误提示,根本不知道是序列化失败还是解码失败。
修复方案:添加错误日志
把try?换成do-catch,打印具体的错误信息,比如修改你原来的扩展:
extension Decodable { init?(with dictionary: [String: Any]) { do { let data = try JSONSerialization.data(withJSONObject: dictionary, options: []) do { self = try JSONDecoder().decode(Self.self, from: data) } catch { print("JSONDecoder解码失败:\(error)") return nil } } catch { print("JSONSerialization失败:\(error)") return nil } } }
这样运行时就能看到具体的错误,比如哪个类型不支持,哪个属性解码失败。
4. 测试单个文档的解码,缩小问题范围
你可以先单独测试一个Firestore文档的解码,看看是不是所有文档都有问题,还是个别文档的问题。修改getNotifications函数:
public func getNotifications( completion: @escaping ([IGNotification]) -> Void ) { guard let username = UserDefaults.standard.string(forKey: "username") else { completion([]) return } let ref = firestoreDatabase.collection("users").document(username).collection("notifications") ref.getDocuments { snapshot, error in guard let docs = snapshot?.documents, error == nil else { completion([]) return } // 先测试第一个文档的解码 if let firstDoc = docs.first { let docData = firstDoc.data() print("测试文档数据:\(docData)") let testNotification = IGNotification(with: docData) print("测试解码结果:\(testNotification)") } let notifications = docs.compactMap({ IGNotification(with: $0.data()) }) completion(notifications) print("最终结果:\(notifications)") } }
通过这个测试,你能快速定位是所有文档都解码失败,还是某个字段或类型导致的问题。
按照上面的步骤排查,应该能很快找到问题所在。
内容的提问来源于stack exchange,提问作者Swimmer97

