Deserialize:

4 имеетКодируемый и это круто НоUIImage не соответствует ему по умолчанию. Как мы можем сделать это?

Я пробовал сsingleValueContainer а такжеunkeyedContainer

extension UIImage: Codable {
  // 'required' initializer must be declared directly in class 'UIImage' (not in an extension)
  public required init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let data = try container.decode(Data.self)
    guard let image = UIImage(data: data) else {
      throw MyError.decodingFailed
    }

    // A non-failable initializer cannot delegate to failable initializer 'init(data:)' written with 'init?'
    self.init(data: data)
  }

  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    guard let data = UIImagePNGRepresentation(self) else {
      return
    }

    try container.encode(data)
  }
}

Я получаю 2 ошибки

'required' инициализатор должен быть объявлен непосредственно в классе 'UIImage' (не в расширении)Неисправный инициализатор не может делегировать отказавшему инициализатору init (data :), записанному с init?

Обходной путь должен использовать обертку. Но есть ли другие способы?

 Leo Dabus13 сент. 2017 г., 18:13
Если вам нужно сохранить изображение внутри строки JSON, просто преобразуйте данные изображения в строку base64 и сохраните ее как строку
 Hamish13 сент. 2017 г., 19:18
@ onmyway133 Мой главный вопрос - просто спроситьПочему Вы хотели это :) Остальное было предположение, основанное на текущих (и часто используемых) кодировщиках / декодерах, теперь предоставляемыхFoundation.
 onmyway13313 сент. 2017 г., 19:08
@Hamish @LeoDabus Я не упоминаю json или xml в своем вопросе. Я думаю, что вы предлагаетеJSONEncoder? но это только одна реализацияEncoder протокол
 Hamish13 сент. 2017 г., 16:52
Почему именно вы хотите соответствоватьUIImage вCodable? Изображения обычно не являются хорошими кандидатами для кодирования в такие форматы, как JSON или XML. Обычно лучше кодировать изображение отдельно, а затем кодировать, например, URL-адрес в JSON.
 Torongo13 сент. 2017 г., 14:49
Что делать, если вы создаетеsub class изUIImage что подтверждаетCodable и добавить требуемый инициализатор на этом.

Ответы на вопрос(4)

что соответствует Codable, например String.

Чтобы преобразовать UIImage в строку внутриfunc encode(to encoder: Encoder) throws:

let imageData: Data = UIImagePNGRepresentation(image)!
let strBase64 = imageData.base64EncodedString(options: .lineLength64Characters)
try container.encode(strBase64, forKey: .image)

Чтобы преобразовать строку обратно в UIImage внутриrequired init(from decoder: Decoder) throws:

let strBase64: String = try values.decode(String.self, forKey: .image)
let dataDecoded: Data = Data(base64Encoded: strBase64, options: .ignoreUnknownCharacters)!
image = UIImage(data: dataDecoded)
Решение Вопроса
Решение: сверните свой собственный класс оболочки, соответствующий Codable.

UIImage вне, это обернуть изображение в новый класс, который вы имеете. В противном случае ваша попытка в основном прямая. Я видел, что это прекрасно сделано в среде кеширования Hyper Interactive, которая называетсякэш.

Хотя вам нужно посетить библиотеку, чтобы углубиться в зависимости, вы можете получить представление об ихImageWrapper Класс, который построен для использования следующим образом:

let wrapper = ImageWrapper(image: starIconImage)
try? theCache.setObject(wrapper, forKey: "star")

let iconWrapper = try? theCache.object(ofType: ImageWrapper.self, forKey: "star")
let icon = iconWrapper.image
Вот их класс оболочки:
// Swift 4.0
public struct ImageWrapper: Codable {
  public let image: Image

  public enum CodingKeys: String, CodingKey {
    case image
  }

  // Image is a standard UI/NSImage conditional typealias
  public init(image: Image) {
    self.image = image
  }

  public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let data = try container.decode(Data.self, forKey: CodingKeys.image)
    guard let image = Image(data: data) else {
      throw StorageError.decodingFailed
    }

    self.image = image
  }

  // cache_toData() wraps UIImagePNG/JPEGRepresentation around some conditional logic with some whipped cream and sprinkles.
  public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    guard let data = image.cache_toData() else {
        throw StorageError.encodingFailed
    }

    try container.encode(data, forKey: CodingKeys.image)
  }
}

Я хотел бы услышать, что вы в конечном итоге используете.

ОБНОВИТЬ: ОказываетсяОП написал код на который я ссылался (обновление Swift 4.0 до Cache) для решения проблемы. Конечно, код заслуживает того, чтобы быть здесь, но я также оставлю свои слова неотредактированными для драматической иронии всего этого. :)

 onmyway13321 сент. 2017 г., 07:23
Благодарю. Знаете ли вы, что я реализовал это, пожалуйста, смотрите коммиты
 AmitaiB24 сент. 2017 г., 19:31
Ах, хорошо, я, конечно, теперь знаю! Мне не приходило в голову, что мир может быть таким маленьким. Урок выучен. Хм, разве это не значит, что «мой» ответ принят, хммм?
 onmyway13325 сент. 2017 г., 11:00
Привет, я хотел бы посмотреть, есть ли более умное решение, чем мое

самый простой способ - просто сделать это.Data вместоUIImage:

public struct SomeImage: Codable {

    public let photo: Data

    public init(photo: UIImage) {
        self.photo = photo.pngData()!
    }
}

Deserialize:

UIImage(data: instanceOfSomeImage.photo)!

используя расширение дляKeyedDecodingContainer а такжеKeyedEncodingContainer классы:

enum ImageEncodingQuality: CGFloat {
    case png = 0
    case jpegLow = 0.2
    case jpegMid = 0.5
    case jpegHigh = 0.75
}

extension KeyedEncodingContainer {

    mutating func encode(_ value: UIImage,
                         forKey key: KeyedEncodingContainer.Key,
                         quality: ImageEncodingQuality = .png) throws {
        var imageData: Data!
        if quality == .png {
            imageData = UIImagePNGRepresentation(value)
        } else {
            imageData = UIImageJPEGRepresentation(value, quality.rawValue)
        }
        try encode(imageData, forKey: key)
    }

}

extension KeyedDecodingContainer {

    public func decode(_ type: UIImage.Type, forKey key: KeyedDecodingContainer.Key) throws -> UIImage {
        let imageData = try decode(Data.self, forKey: key)
        if let image = UIImage(data: imageData) {
            return image
        } else {
            throw SDKError.imageConversionError
        }
    }

}

Вот пример использования:

class DocumentScan: Codable {

    private enum CodingKeys: String, CodingKey {
        case scanDate
        case image
    }

    let scanDate: Date
    let image: UIImage

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        scanDate = try container.decode(Date.self, forKey: .scanDate)
        image = try container.decode(UIImage.self, forKey: .image)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(scanDate, forKey: .scanDate)
        try container.encode(image, forKey: .image, quality: .png)
    }
}

PS: Вы можете использовать такой способ усыновленияCodable к любому типу класса

Ваш ответ на вопрос