0
votes

I'm having difficulty encoding and loading transformable color data from a plist that previously loaded without difficulty from code.

In an extension of UIColor, I've a limited number of available colours in a struct plus my encoder and decoder.

extension UIColor
{
struct Words
{
    static let verb = UIColor(netHex: 0xff0000)
    static let noun = UIColor.blue
    static let subject = UIColor(netHex: 0x0000ff)
    static let object = UIColor(netHex: 0x1e5900)
    static let indirectObject = UIColor(netHex: 0x369d01)
    static let genitive = UIColor(netHex: 0x783f04)
    static let article = UIColor(netHex: 0xff6500)
    static let adjective = UIColor(netHex: 0x9900ff)
    static let adverb = UIColor(netHex: 0xff00ff)
    static let generic = UIColor(netHex: 0x70747a)
}

class func color(data:Data) -> UIColor? {
    return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIColor
}

func encode() -> Data? {
    return try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
}
}

In my code model (this works), I simply defined the color's text value:

var color1: String?
... 
color1 = subject // etc

And in a method, the encoded data is loaded into the DB:

let myColorData = myColor1.encode()

And then in a collection view cellForItemAt indexPath, I can access the color data, which I use to attribute text (not shown).

let color1 = UIColor.color(data: cellData.color1 as! Data)

The problem occurs when, instead of loading the data from code, I load it from a pList. The data seems to load into Core Data, but the app crashes anyway, with an error:

  • Could not cast value of type 'NSTaggedPointerString' (0x108cc0560) to 'UIColor'

I think this means UIColor is not being encoded(?), although it does seem to load something 'blob' into the DB.

If I follow advice from Make UIColor Codable, and similar, but I keep getting the error.

  • Referencing instance method 'encode(to:)' on 'Optional' requires that 'UIColor' conform to 'Encodable'.

I guess I need to make my struct Words: Codable, but I'm not sure if that is true, nor what is required to do this.

Could someone please advise what I need to do in this case how to make UIColor encodable in the way I need it to?

1
You can’t. You need to create your own color structure.Leo Dabus

1 Answers

2
votes

Why do you want UIColor additionally conform to Codable? It conforms already to NSSecureCoding so it's serializable to Data by default.

And even with your implementation you can encode a color

let encoded = UIColor.Words.adverb.encode()!

and decode it

let color = UIColor.color(data: encoded)

And particularly in Core Data you can use computed properties to transform a supported type to an unsupported and vice versa.

My recommedation for Core Data is to save the color as Int32 (the hex representation) or as (hex) String


This is an implementation I'm using in Core Data, an extension to convert the color to a hex string

extension UIColor {

    private func float2String(_ float : CGFloat) -> String {
        return String(format:"%02X", Int(round(float * 255)))
    }

    var hex : String {
        var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0
        getRed(&red, green: &green, blue: &blue, alpha: nil)
        return "#" + float2String(red) + float2String(green) + float2String(blue)
    }

    convenience init(hex : String) {
        let hex = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex
        if hex.count == 6, hex.range(of: "[^0-9A-Fa-f]", options: .regularExpression) == nil {
            let chars = Array(hex)
            let numbers = stride(from: 0, to: chars.count, by: 2).map() {
                CGFloat(strtoul(String(chars[$0 ..< min($0 + 2, chars.count)]), nil, 16))
            }
            self.init(red: numbers[0] / 255, green: numbers[1] / 255, blue: numbers[2] / 255, alpha: 1.0)
        } else {
            self.init(white: 1.0, alpha: 1.0)
        }
    }
}

and the relevant part of the NSManagedObject subclass

@NSManaged public var hexColor: String


var color : UIColor {
    get { return UIColor(hex: hexColor) }
    set { hexColor = newValue.hex }
}