为 UIColor 实现 Codable
Implementing Codable for UIColor
是否可以为 UIColor
实现 Encodable
和 Decodable
属性
当我尝试添加 Decodable
扩展程序时出现错误
extension UIColor : Decodable {
public required init(from decoder: Decoder) throws {
self.init(red: 1, green: 1, blue: 1, alpha: 1)
}
}
error: ColorStuff.playground:98:21: error: initializer requirement 'init(from:)' can only be satisfied by a required
initializer in the definition of non-final class 'UIColor'
public required init(from decoder: Decoder) throws {
我是不是漏掉了什么明显的东西?
我对 Encodable
扩展没有任何问题 - 它似乎是一个 Decodable
问题。
错误消息暗示我无法执行此操作,因为无法访问 UIColor
class 定义
由于编译器给出的错误,您无法在扩展中使 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([=10=]) }))
let colors2 = try! JSONDecoder().decode([WrapperOfNSCoding<UIColor>].self, from: jsonData).map({ [=10=].wrapped })
print(colors2)
尽管需要初始化,但仍有方法可以使 UIColor
Codable
。
您可以扩展 Codable
本身,以便 UIColor
开始自动符合它。
import UIKit
extension Decodable where Self: NSSecureCoding {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
guard let object = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Self else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Invalid object"
)
}
self = object
}
}
extension Encodable where Self: NSSecureCoding {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)
try container.encode(data)
}
}
extension UIColor: Codable { }
检查一下
import XCTest
class CodingTextCase: XCTestCase {
let encoder = JSONEncoder()
let decoder = JSONDecoder()
func testUIColor() throws {
let colorAsJSON = try encoder.encode(UIColor.red)
print(String(data: colorAsJSON, encoding: .utf8)!)
let uiColor = try? decoder.decode(UIColor.self, from: colorAsJSON)
XCTAssertEqual(uiColor!, UIColor.red)
}
}
CodingTextCase.defaultTestSuite.run()
但请注意,在这种情况下,UIColor 实例的数据将占用大约 500 个字节并将其存储为 RGBA 。
是否可以为 UIColor
Encodable
和 Decodable
属性
当我尝试添加 Decodable
扩展程序时出现错误
extension UIColor : Decodable {
public required init(from decoder: Decoder) throws {
self.init(red: 1, green: 1, blue: 1, alpha: 1)
}
}
error: ColorStuff.playground:98:21: error: initializer requirement 'init(from:)' can only be satisfied by a
required
initializer in the definition of non-final class 'UIColor' public required init(from decoder: Decoder) throws {
我是不是漏掉了什么明显的东西?
我对 Encodable
扩展没有任何问题 - 它似乎是一个 Decodable
问题。
错误消息暗示我无法执行此操作,因为无法访问 UIColor
class 定义
由于编译器给出的错误,您无法在扩展中使 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([=10=]) }))
let colors2 = try! JSONDecoder().decode([WrapperOfNSCoding<UIColor>].self, from: jsonData).map({ [=10=].wrapped })
print(colors2)
尽管需要初始化,但仍有方法可以使 UIColor
Codable
。
您可以扩展 Codable
本身,以便 UIColor
开始自动符合它。
import UIKit
extension Decodable where Self: NSSecureCoding {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
guard let object = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Self else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Invalid object"
)
}
self = object
}
}
extension Encodable where Self: NSSecureCoding {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)
try container.encode(data)
}
}
extension UIColor: Codable { }
检查一下
import XCTest
class CodingTextCase: XCTestCase {
let encoder = JSONEncoder()
let decoder = JSONDecoder()
func testUIColor() throws {
let colorAsJSON = try encoder.encode(UIColor.red)
print(String(data: colorAsJSON, encoding: .utf8)!)
let uiColor = try? decoder.decode(UIColor.self, from: colorAsJSON)
XCTAssertEqual(uiColor!, UIColor.red)
}
}
CodingTextCase.defaultTestSuite.run()
但请注意,在这种情况下,UIColor 实例的数据将占用大约 500 个字节并将其存储为 RGBA