CoreData transient transformable image attribute that supports iOS and macOS

234 views Asked by At

I have a transient attribute that holds a pointer to an image. The class of that image is specified in the core data model file as UIImage but I need to also support NSImage.

How would I support both if the model file is asking me for the class name and the module name?

enter image description here

EDIT

My model is backed by core data, so I am using a scheme that Apple recommends to ensure my local persistence plays nicely with iCloud.

enter image description here

2

There are 2 answers

0
bobby123uk On

I used a typealias for the type.

#if os(iOS)
import UIKit
public typealias RNImage = UIImage
#elseif os(macOS)
import Cocoa
public typealias RNImage = NSImage
#endif

final class ImagePersistenceTransformer: NSSecureUnarchiveFromDataTransformer {
    
    override class func allowsReverseTransformation() -> Bool {
        return true
    }
    
    override class func transformedValueClass() -> AnyClass {
        return RNImage.self
    }
    
    override class var allowedTopLevelClasses: [AnyClass] {
        return [RNImage.self]
    }

    override func transformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else {
            fatalError("Wrong data type: value must be a Data object; received \(type(of: value))")
        }
        return super.transformedValue(data)
    }
    
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let image = value as? RNImage else {
            fatalError("Wrong data type: value must be an RNImage object; received \(type(of: value))")
        }
        return super.reverseTransformedValue(image)
    }
}

enter image description here

3
Tom Harrington On

If it was me I'd change it from a transformable to a binary data property, and then save PNG or JPG data in the property. Transformable isn't a good choice here because it encodes the class name with the image data (along with whatever other data the class decides to include when encoding itself).

With UIImage this is pretty simple-- you can use pngData() to get the data and init(data:) to convert back. With NSImage it's slightly less convenient but not hard. There are numerous examples here on SO that explain the steps, for example here.