Обобщения, которые могут реализовать как Void, так и Codable - PullRequest
2 голосов
/ 03 июля 2019

Здесь у меня есть структура, которую я использую для результатов API

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

Теперь, обычно, чтобы это соответствовало Codable, это означало бы, что объект Result должен быть Codable.Это будет выглядеть следующим образом:

struct Response<Result: Codable>: Codable {...}

// or

extension Response: Codable where Result: Codable {}

Проблема, которую я получаю, состоит в том, что некоторые ответы не имеют ключа result, и я хочу иметь возможность использовать объект Responseкак с типом Void вместо Response<Void> очень похоже на так что вопрос .

В настоящее время у меня есть возможность обойти это, просто объявить другой тип Response без result переменная внутри него выглядит так:

struct BaseResponse {
    let status: Int
    let message: String
}

Есть ли способ обойти это, чтобы мне не нужно было объявлять другой тип Response?


Я пыталсяделает следующее, но ничего не работает

  1. Я не могу соответствовать Void Codable
  2. Иметь другое расширение расширения для codable где Result: Void
extension Response: Codable where Result: Codable {}

extension Response: Codable where Result: Void {}
Never также не будет работать, потому что у него нет собственного инициализатора, поэтому я не могу согласовать его с Codable Создать тип Nothing, который соответствует Codableэто
struct Nothing: Codable, Hashable {
    init() {}

    static var nothing: Nothing { return .init() }
}

Так что я могу использовать ответ, подобный этому

let response: Response<Nothing> = Response(
    status: 200,
    message: "Success",
    result: .nothing
)

or

public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.status = try container.decode(forKey: .status)
    self.message = try container.decode(forKey: .message)

    // These lines don't work.
    if let result = try container.decodeIfPresent(Result.self, forKey: .result) {
        self.result = result
    } else {
        self = .nothing
    }
}

Но дело в том, что у меня не может быть decodeIfPresent метод, специфичный для Nothingтип.Так много для этого.

Ответы [ 2 ]

1 голос
/ 03 июля 2019

Вы можете определить свой init(from:) метод следующим образом:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    status = try container.decode(Int.self, forKey: .status)
    message = try container.decode(String.self, forKey: .message)

    if Result.self is Nothing.Type {
        result = Nothing() as! Result
    } else {
        result = try container.decode(Result.self, forKey: .result)
    }
}

Вы обнаруживаете, что находитесь в случае Response<Nothing>, и полностью пропускаете декодирование результата. Таким образом, вы сохраняете свое обычное result декодирование в случае, когда требуется результат, и можете оставить его необязательным.

1 голос
/ 03 июля 2019

Сделать result как optional тип Result?.Теперь, когда ответ содержит значение для ключа result, оно будет установлено в свойстве result.В противном случае он останется nil, если ключ result отсутствует.

struct Response<Result: Codable>: Codable {
    let status: Int
    let message: String
    let result: Result? //here....
}

Анализ , например,

do {
    let response = try JSONDecoder().decode(Response<String>.self, from: data)
    print(response)
} catch {
    print(error)
}

Используйте любой тип, который выхотите в Response<String> согласно вашему требованию.

Пример:

  1. Если JSON равно {"status":1,"message":"This is a message","result":"QWERTY"}

тогда response есть, Response<String>(status: 1, message: "This is a message", result: Optional("QWERTY"))

Если JSON равно {"status":1,"message":"This is a message"}

, тогда response равно Response<String>(status: 1, message: "This is a message", result: nil))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...