Как я могу использовать единую структуру модели для данных из нескольких API / источников в Swift? - PullRequest
0 голосов
/ 12 апреля 2020

Допустим, я хочу получать новостные статьи с сайта A и сайта B, каждая с разным URL и различной структурой JSON полезных нагрузок. Вместо двух разных моделей управления я хочу объединить их в единую модель данных «Статьи», которая получает данные от каждого из API. Я хочу знать, если это возможно, используя CodingKeys или словарь или что-то.

Я хочу сделать что-то вроде:

struct Article: Codable, Hashable {
    var title: String
    var site: String
    var date: String
    var image: String
    var url: String
}

, а затем иметь CodingKeys для преобразования JSON из каждый сайт в общую модель. Что-то вроде:

extension Article {
    enum CodingKeys: String, CodingKey {
        var title = "title"
        var site = "site_name"
        var date = "date_published"
        var image = "featured_image_url"
        var url = "article_url"
}

Но проблема с приведенным выше кодом заключается в том, что каждый JSON отличается, с разными именами для объектов. Итак, я хочу сделать что-то вроде:

enum DataSource {
    case siteA
    case siteB
}

extension Article {
    enum CodingKeys: String, CodingKey {
        switch DataSource {
        case siteA:
            var title = "title"
            var site = "site_name"
            var date = "date_published"
            var image = "featured_image_url"
            var url = "article_url"
        case siteB:
            var title = "article_title"
            var site = "site_name_long"
            var date = "published_date"
            var image = "featured_image"
            var url = "url"
}

и затем, конечно, сделать все вещи JSONdecoder ниже этого. Как я могу это сделать?

1 Ответ

1 голос
/ 12 апреля 2020

Вы можете передать параметр в decoder, используя userInfo. Вы можете контролировать способ декодирования любым способом, установив дополнительный параметр с помощью userInfo. Здесь я передаю Article.Site.a/b на site ключ.

Определение модели:

// Model
struct Article: Codable, Hashable {

    var title: String
    var site: String
    var date: String
    var image: String
    var url: String

    enum Site {
        case a
        case b
    }
    enum SiteError : Error {
        case unknownSite
    }
    enum SiteACodingKeys: String, CodingKey {
        case title
        case site
        case date
        case image
        case url
    }
    enum SiteBCodingKeys:String, CodingKey{
        case title = "article_title"
        case site = "article_site"
        case date = "article_date"
        case image = "article_image"
        case url = "article_url"
    }


    init(from decoder: Decoder) throws {

        guard   let key = CodingUserInfoKey(rawValue: "site"),
                let value = decoder.userInfo[key],
                let site =  value as? Site else {
            throw SiteError.unknownSite
        }

        switch site {
        case .a:
            let container = decoder.container(keyedBy: SiteACodingKeys)

            title = try? container.decode(String.self, forKey: .title)
            // decode other properties

        case .b:
            let container = decoder.container(keyedBy: SiteBCodingKeys)

            title = try? container.decode(String.self, forKey: .title)
            // decode other properties
        }
    }
}

Декодирование:

// Decoding
let decoder = JSONDecoder()
decoder.userInfo[CodingUserInfoKey(rawValue: "site")] = Article.Site.a // Set `Article.Site.b` while data is from site B
// Now use this decoder to decode JSON
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...