Reputation: 351
I'm trying to setup validation in core data to a simple managed object structure.
There is a Building
and Floor
and a one-to-many relationship between both (on Building has many Floors).
The validator in the Floor
looks like this
// extension of auto generated managed object (Floor)
extension Floor {
override public func validateForInsert() throws {
try super.validateForInsert()
if (self.building == nil) {
throw ValidationError.missingBuilding("a floor requires a building")
}
}
}
And my simple manager for testing:
import Foundation
import CoreData
class ObjectNodeManager {
let persistentContainer: NSPersistentContainer!
init(container: NSPersistentContainer) {
self.persistentContainer = container
self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
}
lazy var backgroundContext: NSManagedObjectContext = {
return self.persistentContainer.newBackgroundContext()
}()
func insertObjectNode(objectNode: ObjectNode) -> ObjectNode? {
backgroundContext.insert(objectNode)
return objectNode
}
func save() {
if backgroundContext.hasChanges {
do {
try backgroundContext.save()
} catch {
fatalError("save error \(error)")
}
}
}
}
When I try to create an save a new building and floor I get an exception.
let building = Building(context: manager.backgroundContext)
let floor = Floor(context: manager.backgroundContext)
floor.building = building // VALIDATION EXCEPTION!
// a basic backgroundContext.save() (persistentContainer) in the manager
manager.save()
With the validationForInsert
it is impossible for me to insert/save the managed objects. Alternatively I could use validationForUpdate
but this would be too late, since I do not want to store a Floor
without the proper relationship to the Building
.
Any suggestion how to save manage object with required relationships and validation on insert?
Thank you.
Upvotes: 1
Views: 283
Reputation: 5030
When I first started using Core Data in 2009, I ran into similar false alarm problems. My solution was and still is: Do not use Core Data's validation in your top-level main managed object context. That is, do not implement validateForInsert()
. Even if Core Data does properly flag a true alarm, as you have seen, it occurs during saving, which is too late to do anything intelligent about it. Users are not happy when their work cannot be saved.
You may wish to use Core Data's validation in a child managed object context. In this case, you are typically in a view controller, the validation error is caught immediately after the user entered it, and your immediate presentation of the error will make sense to the user.
Upvotes: 1
Reputation: 1767
The manager.background
context always returns a new context, your building
and floor
exist in two different contexts.
let backgroundContext = manager.backgroundContext
let building = Building(context: backgroundContext)
let floor = Floor(context: backgroundContext)
floor.building = building
manager.save()
Upvotes: 1