【问题标题】:Best approach to create Non-optional Codable with `default values` in swift在 swift 中使用“默认值”创建非可选 Codable 的最佳方法
【发布时间】:2020-02-26 11:58:20
【问题描述】:

我知道classstruct 的基本概念,但是为API 创建模型以获取数据并告诉我优缺点更有效。

以前我不使用可选的模型。相反,我给它一些价值。即

class CompanyInfo : Codable {
    var NameEn : String = ""
    var CityEn : String = ""
    var Website : String = ""
    var Email : String = ""
    var Phone : String = ""
    var Fax : String = ""
}

但是当它从 API 获得一些 null 值时。即"Fax": null 然后应用程序崩溃,因为它无法使用以下行解析数据

let data = try JSONDecoder().decode(dataModel.self, from: dataSet)

什么是定义模型的最佳方式,所以我不需要解开可选或给它默认值。

【问题讨论】:

  • 你做不到。
  • @wahab 您可以使用“Glossy”或任何其他库,我们不需要手动创建数据模型,只需给他们 JSON,他们会为我们创建它。如果您需要示例,请告诉我。
  • 您可以这样做,但是您必须在任何地方实现init(from decoder: Decoder) throws 以分配默认值,以防值不存在。始终以struct 开头,并在需要时将其更改为class。您也可以查看this了解一些相关信息。
  • @MojtabaHosseini 谢谢,我不知道decodeIfPresent(_:forKey:)。这是题外话,但你最终必须处理默认值,躲避选项是不可能的:P

标签: ios swift xcode codable decodable


【解决方案1】:

您可以使用默认值实现自定义解码器:

class CompanyInfo : Codable {
    var NameEn: String
    var CityEn: String
    var Website: String
    var Email: String
    var Phone: String
    var Fax: String
    
    required init(from decoder: Decoder) throws {
        do {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.NameEn = try container.decodeIfPresent(String.self, forKey: .NameEn) ?? "Default"
            self.CityEn = try container.decodeIfPresent(String.self, forKey: .CityEn) ?? "Default"
            self.Website = try container.decodeIfPresent(String.self, forKey: .Website) ?? "Default"
            self.Email = try container.decodeIfPresent(String.self, forKey: .Email) ?? "Default"
            self.Phone = try container.decodeIfPresent(String.self, forKey: .Phone) ?? "Default"
            self.Fax = try container.decodeIfPresent(String.self, forKey: .Fax) ?? "Default"
        }
    }
}

与问题无关,但很重要的注意事项:

在 Swift 中,只有 Types 名称应该以大写字母开头。如果你继续这样命名变量,如果你决定使用CoreData 或与其他 Swift 开发人员合作,那么有一天你会遇到严重的重构问题。

【讨论】:

  • 抱歉我的回复晚了。我正在尝试您的解决方案,但我遇到了另一个问题,我应该为我的通用类 Data<T> 分配默认值吗?
  • 这完全是另一个问题。请随时ask a new question。你在这里评论链接,所以我可以检查我是否可以帮助你。
【解决方案2】:

如果数据模型反映了 API 的 JSON 响应(“不要让我思考”),任何未来的同事都会感谢您。此外,现在您不希望有可选值 - 在 3 周内您可能需要它们 - 然后您有一些丑陋的检查:

if companyInfo.fax == "default" {
   // Hey it's the default value but this indicates that the value is optional and nil
}

但是,这是可行的:

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

顺便说一句 - 还可以阅读有关属性名称的 Swift 命名约定。

https://swift.org/documentation/api-design-guidelines/

【讨论】:

    【解决方案3】:

    对于更有效地使用classstruct,没有这样的答案。这取决于您的需求、应用程序的需求及其编码结构。

    如果您必须在运行时处理选项,这可能是我认为的最佳方法。

    我更愿意在此使用struct

    struct YOUR_MODEL_NAME : Codable {
    
        var NameEn : String?
        var CityEn : String?
        var Website : String?
        var Email : String?
        var Phone : String?
        var Fax : String?
    
        enum CodingKeys: String, CodingKey {
            case NameEn = "YOUR_KEY_FOR_NameEn"
            case CityEn = "YOUR_KEY_FOR_CityEn"
            case Website = "YOUR_KEY_FOR_Website"
            case Email = "YOUR_KEY_FOR_Email"
            case Phone = "YOUR_KEY_FOR_Phone"
            case Fax = "YOUR_KEY_FOR_Fax"
        }
    
        init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            NameEn = try values.decodeIfPresent(String.self, forKey: .NameEn)
            CityEn = try values.decodeIfPresent(String.self, forKey: .CityEn)
            Website = try values.decodeIfPresent(String.self, forKey: .Website)
            Email = try values.decodeIfPresent(String.self, forKey: .Email)
            Phone = try values.decodeIfPresent(String.self, forKey: .Phone)
            Fax = try values.decodeIfPresent(String.self, forKey: .Fax)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-10
      • 1970-01-01
      • 2017-08-24
      相关资源
      最近更新 更多