使用 Codable 和 Firestore 的非对称 Encoding/Decoding?

Asymmetric Encoding/Decoding with Codable and Firestore?

我有以下结构:

struct Recipe: Codable {
    @DocumentID var id: String?
    var vegetarian: Bool?
    
    private enum CodingKeys: String, CodingKey {
        case id
        case vegetarian
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(vegetarian, forKey: .vegetarian)
    }
}

我正在尝试:

这是我的数据模型:

docRef.getDocument { document, error in
            if let error = error as NSError? {
                self.errorMessage = "Error getting document: \(error.localizedDescription)"
            }
            else {
                if let document = document {
                    do {
                        self.recipe = try document.data(as: Recipe.self)
                        
                        let recipeFromFirestore = Recipe(
                            id: self.recipe!.id,
                            vegetarian: self.recipe!.vegetarian)
                        
                        self.recipes.append(recipeFromFirestore)
                    }
                    catch {
                        print("Error: \(error)")
                    }
                }
            }
        }

我的 getDocument 中出现以下错误:Missing argument for parameter 'from' in call

如果我在结构中注释掉 init(from decoder: Decoder)func encode(to encoder: Encoder),则不会发生此错误。我应该为这种不对称 encoding/decoding 做些不同的事情吗?

我不明白你是从哪里看到这个错误的 - 行参考会很有用 - 但它与你的 en/decoding 没有直接关系。 (顺便说一句,该方法确实不是数据模型)

因为你想用匹配它们 属性 名称的 JSON 键来解码这两个属性,所以不需要指定 CodingKeys 或编写自定义解码器;您可以依靠合成解码器,让 Codable 为您完成工作。

对于解码,您将需要一个自定义解决方案,否则 Decodable 将解码这两个字段。这将需要一个 CodingKey 枚举(注意单数,即协议,而不是默认的 enun 名称)和一个自定义编码器来使用它。

你最终得到了一个更简单的结构实现。我还添加了一个简单的初始化程序,因为一旦定义 init(from:),您就会丢失合成的成员初始化程序。这只是为了让我可以测试一下。

struct Recipe: Codable {
   var id: String?
   var vegetarian: Bool?
   
   init(id: String, vegetarian: Bool){
      self.id = id
      self.vegetarian = vegetarian
   }
   
   init(from decoder: Decoder) throws {
      
      enum DecodingKeys: CodingKey {
         case vegetarian
      }
      
      let container = try decoder.container(keyedBy: DecodingKeys.self)
      vegetarian = try container.decode(Bool.self, forKey: .vegetarian)
   }
}

如果您对此进行测试,您会发现它只会对 vegetarian 属性 进行解码,但会对两者进行编码。简单测试显示:

let recipe = Recipe(id: "1", vegetarian: true)
let data = try! JSONEncoder().encode(recipe)
print(String(data: data, encoding: .utf8)!)  //{"id":"1","vegetarian":true}

let decodedRecipe = try! JSONDecoder().decode(Recipe.self, from: data)
print(decodedRecipe) //Recipe(id: nil, vegetarian: Optional(true))