iOS端REST API请求Firestore时引用数组的Codable解码问题
解码Firestore REST API返回的引用类型数组方案
你解码失败的核心原因是没有匹配listaRefsLinea的多层嵌套结构:这个字段外层是arrayValue包装,内部values数组的每个元素又被referenceValue包装,和普通字符串字段的单层stringValue结构不一样,按层级逐次解码即可,不需要依赖Firebase SDK。
第一步:补充对应字段结构的Codable包装类型
先加两个辅助结构体,匹配Firestore返回的数组、引用类型包装格式:
// 匹配Firestore数组字段的外层包装 struct ArrayValue: Codable { let values: [ReferenceValue] } // 匹配Firestore文档引用字段的包装 struct ReferenceValue: Codable { let refPath: String private enum CodingKeys: String, CodingKey { case refPath = "referenceValue" } }
第二步:替换原有占位的listaRefsLinea解码逻辑
把你代码里listaRefsLinea = [""]这行替换成下面的代码,逐层解包:
// 先解出arrayValue层 let arrayWrapper = try fieldContainer.decode(ArrayValue.self, forKey: .listaRefsLinea) // 遍历数组元素,提取每个referenceValue里的路径字符串 listaRefsLinea = arrayWrapper.values.map(\.refPath)
注意:你原来BrandKeys枚举里多写了一个listaRefsLinea的case,这个字段是在fields嵌套容器里的,外层容器不存在这个键,记得删掉,否则解码其他字段时可能出错。
修正后的完整解码代码
import Foundation struct BrandResponse : Codable { let brands : [Brand_Struct] private enum CodingKeys : String, CodingKey { case brands = "documents" } } struct StringValue : Codable { let value : String private enum CodingKeys : String, CodingKey { case value = "stringValue" } } struct ArrayValue: Codable { let values: [ReferenceValue] } struct ReferenceValue: Codable { let refPath: String private enum CodingKeys: String, CodingKey { case refPath = "referenceValue" } } struct Brand_Struct : Codable { let url_logo : String let web_url : String let nome_brand : String let descrizione : String let listaRefsLinea : [String] let descrizione_en : String let data_consumption : String private enum BrandKeys : String, CodingKey { case fields } private enum FieldKeys : String, CodingKey { case url_logo case web_url case nome_brand case descrizione case listaRefsLinea case descrizione_en case data_consumption } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: BrandKeys.self) let fieldContainer = try container.nestedContainer(keyedBy: FieldKeys.self, forKey: .fields) nome_brand = try fieldContainer.decode(StringValue.self, forKey: .nome_brand).value web_url = try fieldContainer.decode(StringValue.self, forKey: .web_url).value url_logo = try fieldContainer.decode(StringValue.self, forKey: .url_logo).value descrizione = try fieldContainer.decode(StringValue.self, forKey: .descrizione).value descrizione_en = try fieldContainer.decode(StringValue.self, forKey: .descrizione_en).value data_consumption = try fieldContainer.decode(StringValue.self, forKey: .data_consumption).value let arrayWrapper = try fieldContainer.decode(ArrayValue.self, forKey: .listaRefsLinea) listaRefsLinea = arrayWrapper.values.map(\.refPath) } }
扩展说明
Firestore REST API返回的所有字段都遵循{类型标识: 实际值}的固定包装格式:
- 字符串:
{"stringValue": "内容"} - 数字:
{"integerValue"/"doubleValue": "数值"} - 布尔值:
{"booleanValue": true/false} - 时间戳:
{"timestampValue": "时间字符串"} - 数组:
{"arrayValue": {"values": [子元素]}} - 引用:
{"referenceValue": "文档全路径"}
后续如果需要解码其他类型字段,只需要按照对应结构新增对应的包装结构体即可,不需要引入额外SDK。
解码后拿到的listaRefsLinea元素是文档全路径,如果只需要末尾的文档ID,直接对字符串做split(separator: "/").last截取就行。
内容的提问来源于stack exchange,提问作者MatBuompy
相关产品推荐
相关产品推荐

