2

How does one save and retrieve a generic Measurement in Core Data?

What I'm looking to do is save either a Measurement<UnitMass> or a Measurement<UnitVolume>.

As can be seen in the image below CoreData is set to accept a Generic Measurement<Unit>

Generic Measurement in Core Data

However I'm getting an error when I go to set the value of the measure. Saying that I'm not allowed to do this. I thought the purpose of generics was to support uses like this.

What am i missing?

enter image description here

aaronium112
  • 2,992
  • 4
  • 35
  • 50

2 Answers2

6

The error about mismatched types isn't the important part here. The real problem is that transformable attributes only work with classes that conform to NSCoding or for which you've written your own custom value transformer. Since Measurement is not a class and does not conform to NSCoding, you can't use it with a transformable attribute.

Your options are

  1. Don't save the Measurement, save its values, and convert to/from the Measurement when saving/reading property values.
  2. Write your own custom subclass of ValueTransformer that will convert between Measurement and Data.

I'd go with #1. You could add convenience methods on your managed object subclass to handle the conversion.

Update: Using your Measurement<UnitMass> case, I'd do something like:

  • Give the attribute a Double property named massValue.
  • Give the attribute a transformable property named massUnit with custom class UnitMass (see below).
  • Save values with something like this:

    let servingMeasure = Measurement<UnitMass>(value:500, unit:.grams)
    myObject.massValue = servingMeasure.value
    myObject.massUnit = servingMeasure.unit
    
  • Retrieve values with something like:

    if let unit = myObject.massUnit {
        let value = myObject.massValue
        let measurement = Measurement<UnitMass>(value:value, unit:unit)
        print("Measurement: \(measurement)")
    }
    

This is how the massUnit property is configured:

enter image description here

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • Thanks Tom, I just tested saving Measurement to Core Data and it crashed Xcode twice in a row, so looks like you're right about being unable to save it in Core Data. How would you save the unit value from measurement? I've tried saving a transformable with the Custom Class set to UnitMass but Xcode crashes when i save the context. Perhaps i need a value transformer? I suppose you could save "unit" as Strings (grams, kg, oz) but i'm looking for a solution that will support localization of units. – aaronium112 Jan 30 '18 at 20:07
  • Added much more detail. – Tom Harrington Jan 30 '18 at 20:38
  • Hey Tom, Great answer. I just made a similar property with UnitTemperature. However I'm getting this in the log: `[general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release`. Do you have any idea how to fix this? – StonedStudio Jan 01 '21 at 17:44
0

In Swift, Measurement does adopt the code able protocol, and therefore it can be saved in Core Data through a transformable attribute.

The error that you got is actually pretty clear. You can't save a specific Measurement type Measurement<UnitMass> to the generic type Measurement<Unit> in Core Data. You can't do it in the main code, either. The fix is simple, for each attribute specify the specific type for that attribute as the Custom Class.

Yrb
  • 8,103
  • 2
  • 14
  • 44