Условный тип в модели JSON-ответа (Swift 5)

April 14, 2020

Недавно я работал с API запросом, ответом от которого был JSON, в котором одно из значений могло быть либо Bool, либо String.

Ответ от сервера мог быть таким:

{

    "title": "Test",

    "coverImage": false,

    "coverImageFilename": false

}

Или таким:

{

    "title""Test",

    "coverImage""https://website.com/coverimage.jpg",

    "coverImageFilename""coverimage.jpg"

}

В данном случае если у страницы включена Cover Image, даётся ссылка и название файла. Если картинка не установлена - возвращается false.

Проблема заключалась в том, что структура, написанная под этот json-ответ, не позволяла правильно декодить полученный ответ:

struct PageDetails: Codable {

    public let titleString

    public let coverImageBool

    public let coverImageFilenameBool

}

В одном случае coverImage был Bool, в другом - String, что вызывало ошибку.

Решение

struct ValueOrFalse<T:Codable>: Codable {

    let value: T?

    public init(from decoder:Decoder) throws {

        let container = try decoder.singleValueContainer()

        let falseValue = try? container.decode(Bool.self)

        if falseValue == nil {

            value = try container.decode(T.self)

        } else {

            value = nil

        }

    }

}

Использовать её очень просто:

struct PageDetails: Codable {

    public let title: String

    public let coverImage: ValueOrFalse<String>

    public let coverImageFilename: ValueOrFalse<String>

}

let myJson = """

{

    "title": "Test",

    "coverImage": false,

    "coverImageFilename": false

}

"""

do {

    print(try JSONDecoder().decode(PageDetails.self, from: myJson.data(using: .utf8)!))

} catch {

    print(error)

}