Как мне превратить DateFormatter в структуру Swift, чтобы мне не нужно было указывать JSONDecoder? - PullRequest
0 голосов
/ 28 апреля 2018

Я использую Swift 4 для декодирования некоторых JSON из Twitter:

struct Tweet: Codable {

    let id: String
    let createdAt: Date
    let text: String

    enum CodingKeys: String, CodingKey {
        case id = "id_str"
        case createdAt = "created_at"
        case text
    }
}

let decoder = JSONDecoder()

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
decoder.dateDecodingStrategy = .formatted(dateFormatter)

let tweets = try decoder.decode([Tweet].self, from: data!)

Как я могу сделать так, чтобы мой код не запомнил значение decoder.dateDecodingStrategy. В идеале структура Tweet должна знать о своем формате даты с постоянной статической переменной-членом dateFormatter, инициализированной в правильном формате.

Я полагаю, мне нужно как-то использовать init(decoder: Decoder) на Tweet, но я не знаю, как.

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Вы можете расширить Formatter и создать собственный статический DateFormatter.

extension Formatter {
    static let custom: DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
        return formatter
    }()
}

И если вы хотите, чтобы Tweet проанализировал вашу строку даты, вы можете предоставить свой собственный инициализатор декодера следующим образом:

extension Tweet {
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(String.self, forKey: .id)
        text = try container.decode(String.self, forKey: .text)
        createdAt = try Formatter.custom.date(from: container.decode(String.self, forKey: .createdAt))!
    }
}

Это предполагает, что ваша строка даты правильно отформатирована. Если ваша строка даты не гарантирует правильное форматирование, вы можете сделать свойство Date необязательным и удалить принудительную развертку из метода date (from: String).

0 голосов
/ 28 апреля 2018

Как предлагает @Larme в комментариях, вы можете создать подкласс JSONDecoder и переопределить его init метод, где вы устанавливаете dateDecodingStrategy в формат даты Twitter. Вы также должны убедиться, что вы правильно установили locale из DateFormatter, иначе он не сможет правильно декодировать названия дня / месяца. Я предполагаю, что они на английском языке, поэтому я бы предложил использовать Locale en_US_POSIX для вашего жестко закодированного формата даты.

class JSONTweetDecoder: JSONDecoder {
    private static let dateFormatter: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        return dateFormatter
    }()

    override init() {
        super.init()
        self.dateDecodingStrategy = .formatted(JSONTweetDecoder.dateFormatter)
    }
}

Тогда вам просто нужно инициализировать JSONTweetDecoder вместо JSONDecoder при декодировании ответа.

let tweets = try JSONTweetDecoder().decode([Tweet].self, from: data!)
...