Реализация Codable для UIColor

Можно ли реализовать свойства Encodable и Decodable для UIColor

Когда я пытаюсь добавить расширение Decodable, я получаю сообщение об ошибке

extension UIColor : Decodable {
    public required init(from decoder: Decoder) throws {
        self.init(red: 1, green: 1, blue: 1, alpha: 1)
    }
}

ошибка: ColorStuff.playground: 98: 21: ошибка: требование инициализатора 'init (from :)' может быть удовлетворено только инициализатором required в определении неокончательного класса 'UIColor' Public required init (from decoder: Decoder) throws {

Я упустил здесь что-то очевидное?

У меня нет проблем с расширением Encodable - похоже, это проблема Decodable.

Сообщение об ошибке подразумевает, что я не могу этого сделать из-за отсутствия доступа к определению класса UIColor.


person Jeef    schedule 01.02.2018    source источник
comment
Сравните stackoverflow.com/questions/46522572/.   -  person Martin R    schedule 01.02.2018
comment
Вы упускаете эту очевидную ошибку: 'required' initializer must be declared directly in class 'UIColor' (not in an extension)   -  person user28434'mstep    schedule 01.02.2018
comment
Да вот чего я боялся - как сказано в последнем предложении. Так что мы назовем это громким NO WAY JOSE   -  person Jeef    schedule 01.02.2018
comment
Вы всегда можете попытаться создать подкласс и пусть подкласс реализует Codable. Или, может быть, написать для него обертку.   -  person    schedule 01.02.2018


Ответы (1)


Вы не можете заставить UIColor соответствовать Decodable в расширении из-за ошибки, данной компилятором.

Одно из решений - создать тип оболочки Codable и использовать его вместо него.

Поскольку UIColor уже соответствует NSCoding, давайте просто напишем общий тип, чтобы мы могли кодировать и декодировать все, что соответствует NSCoding.

import UIKit

struct WrapperOfNSCoding<Wrapped>: Codable where Wrapped: NSCoding {
    var wrapped: Wrapped

    init(_ wrapped: Wrapped) { self.wrapped = wrapped }

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let data = try container.decode(Data.self)
        guard let object = NSKeyedUnarchiver.unarchiveObject(with: data) else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "failed to unarchive an object")
        }
        guard let wrapped = object as? Wrapped else {
            throw DecodingError.typeMismatch(Wrapped.self, DecodingError.Context(codingPath: container.codingPath, debugDescription: "unarchived object type was \(type(of: object))"))
        }
        self.wrapped = wrapped
    }

    func encode(to encoder: Encoder) throws {
        let data = NSKeyedArchiver.archivedData(withRootObject: wrapped)
        var container = try encoder.singleValueContainer()
        try container.encode(data)
    }
}

let colors = [UIColor.red, UIColor.brown]
print(colors)
let jsonData = try! JSONEncoder().encode(colors.map({ WrapperOfNSCoding($0) }))
let colors2 = try! JSONDecoder().decode([WrapperOfNSCoding<UIColor>].self, from: jsonData).map({ $0.wrapped })
print(colors2)
person rob mayoff    schedule 01.02.2018
comment
Если я попытаюсь использовать вашу (отличную) оболочку с одним значением, она выдаст ошибку при кодировании, то есть - jsonData = try! JSONEncoder (). Encode ([w]) завершается успешно let jsonData = try! Ошибка JSONEncoder (). Encode (w) с let w = WrapperOfNSCoding (colors [0]) - Неустранимая ошибка: 'попробуйте!' выражение неожиданно вызвало ошибку: Swift.EncodingError.invalidValue (WrapperOfNSCoding # 1 ... Почему это должно быть - person daven11; 17.03.2018
comment
Я буду рад помочь вам, если вы разместите свой собственный вопрос верхнего уровня. Не стесняйтесь добавить здесь комментарий со ссылкой на него, если вы это сделаете. - person rob mayoff; 17.03.2018