Core Data Interop
How should JSON
properties be used in conjunction with Core Data?
Discussion
JSON
neatly solves the case where a Swift type needs to be created from a freeform json string (i.e. the json schema is unknown at compile time and subject to change). Furthermore, it allows the json to modelled in a type safe way.
Codable
conformance allows the type to be easily converted to and from Data
. This makes persisting the type to disk as data relatively simple.
However using the JSON
type in the context of Core Data poses challenges. JSON
was designed as a pure Swift type whereas Core Data has is origin in Objective-C.
The issue is that using a property of type JSON
on an NSManagedObject
subclass is not permitted. The compiler produces an error:
class Person: NSManagedObject {
@NSManaged var firstName: String
@NSManaged var lastName: String
@NSManaged var metaData: JSON // Property cannot be marked @NSManaged because its type cannot be represented in Objective-C
}
This is expected since Core Data does not support JSON
out of the box, and it turns out, the metaData
attribute cannot be marked as Transformable
since Swift Enums with associated values (which JSON
is built upon) cannot be represented in Objective-C and so conformance to NSCoding
seems unlikely.
By contrast Swift dictionaries, say of type [String: Any]
can be used on an NSManagedObject
subclass directly since they conform to NSCoding
and so their corresponding model attribute can be marked as Transformable
.
Options
How then should properties of type JSON
be used in the context of Core Data? Or, does Core Data's tight integration with Objective-C mean the JSON
type cannot be used?
Some options which come to mind:
- Add
NSCoding
conformance to JSON
allowing the attribute's type to be Transformable
.
- This does not seem technically possible as mentioned above.
- Use a custom ValueTransformer.
- This is not possible because
JSON
does not derive from NSObject
.
- Add a
toDictionary()
and fromDictionary
methods facilitating [String: Any]
transformations.
- The attribute type will be
Transformable
with a custom setter and getter in the NSManagedObject
's subclass to do the transformation.
- This seems a bit heavy handed. The transformation cost will be paid on each get or set.
Are they other ways to do this? (I am relatively new to Core Data).
Example Object
class Person: NSManagedObject {
@NSManaged var firstName: String
@NSManaged var lastName: String
@NSManaged var metaData: JSON // Property cannot be marked @NSManaged because its type cannot be represented in Objective-C
}
let personAData: Data = """
{
"firstName": "John",
"lastName": "Appleseed",
"metaData": {
"foo": "bar",
"rah": [1, 2, 3]
}
}
""".data(using: .utf8)!
let personBData: Data = """
{
"firstName": "Joe",
"lastName": "Lemon",
"metaData": {
"yaz": {
"tar": 1234.00
}
}
}
""".data(using: .utf8)!
Here the metaData
property needs to hold and store flexible json which makes JSON
a good candidate.