Эффективное структурирование данных с ориентированный подход протокол в Swift


Мне нужно, чтобы интегрировать некоторые сервера вызова API, в котором я могу получить ответ как:

+ Response 200 (application/json)
+ Body
        {
          "code": 0,
          "status": "ok",
          "message": "Verification Message from API",
          "result": {
            "values": [
              {
                "verify_status": true,
                "token_key": "sure1ff45jk7858b8ae88a77fa30"
              }
            ]
          }
        }

Иногда ответ будет как:

+ Response 200 (application/json)
+ Body
        {
          "code": 0,
          "status": "ok",
          "message": "Update Message from API",
          "result": {
            "values": [
              {
                "update_status": true
              }
            ]
          }
        }

Как видите, в основном все поля тела ответ довольно много же. Единственное различие будет в разделе:

"values": [
    {
        "verify_status": true,
        "token_key": "sure1ff45jk7858b8ae88a77fa30"
    }
]

or,

"values": [
    {
        "update_status": true
    }
]

Я следовал протоколу ориентированное программирование (поп) шаблон для моего использования.


Решение:

struct Value {
    let verifyStatus: Bool?
    let updateStatus: Bool?
    let tokenKey: String?
}
extension Value: Codable {
    private enum ValueCodingKeys: String, CodingKey {
        case verifyStatus = "verify_status"
        case updateStatus = "update_status"
        case tokenKey = "token_key"
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: ValueCodingKeys.self)
        try container.encode(verifyStatus, forKey: .verifyStatus)
        try container.encode(updateStatus, forKey: .updateStatus)
        try container.encode(tokenKey, forKey: .tokenKey)
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: ValueCodingKeys.self)
        verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
        updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
        tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
    }
}

protocol Resultable {
    var values: [Value] { get }
}
struct Result: Resultable {
    let values: [Value]
}
extension Result: Codable {
    private enum ResultCodingKeys: String, CodingKey {
        case values
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: ResultCodingKeys.self)
        try container.encode(values, forKey: .values)
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: ResultCodingKeys.self)
        values = try container.decode([Value].self, forKey: .values)
    }
}

protocol Responsable {
    var code: Int { get }
    var status: String { get }
    var message: String { get }
    var result: Result { get }
}
struct Response: Responsable {
    let code: Int
    let status: String
    let message: String
    let result: Result
}
extension Response: Codable {
    private enum ResponeCodingKeys: String, CodingKey {
        case code
        case status
        case message
        case result
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: ResponeCodingKeys.self)
        try container.encode(code, forKey: .code)
        try container.encode(status, forKey: .status)
        try container.encode(message, forKey: .message)
        try container.encode(result, forKey: .result)
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
        code = try container.decode(Int.self, forKey: .code)
        status = try container.decode(String.self, forKey: .status)
        message = try container.decode(String.self, forKey: .message)
        result = try container.decode(Result.self, forKey: .result)
    }
}

Я думаю, что я более-усложнения решения. Есть ли лучшее решение, чтобы достичь этого?

Мне действительно нужно иметь два protocol определенными?



118
0
задан 6 февраля 2018 в 03:02 Источник Поделиться
Комментарии
1 ответ

Ваша интуиция является правильным: вам не нужны ни Resultable или Responsable протокол. Причина в том, что каждый протокол имеет только один тип, который соответствует его. Протоколы для обмена функциями между несколькими типами (с помощью расширения протокола) или определяя набор гарантированных свойств и поведения, общие для группы видов. Но льготы бывают только тогда, когда есть больше чем один тип, соответствующий протокол. Поскольку ваш код имеет только один тип, соответствующий для каждого протокола, то лучше сейчас, чтобы не иметь протокол.

Кроме того, это не часть вашего вопроса, но это может быть стоит исследовать, является ли ваша реализация Response привязан слишком близко к структуру в JSON. Как это, клиенту придется копать довольно далеко в responseresult значение, чтобы получить любую полезную информацию из него. Переиначивая Result доведение информации вперед и сделать вещи намного легче на клиенте. Например:

enum Result {
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
}

struct Response {
let code: Int
let status: String
let message: String
let result: Result
}

В Codable реализация будет сложнее, но я думаю, что большей простотой по призыву сайте стоит усилий.

1
ответ дан 7 февраля 2018 в 04:02 Источник Поделиться