Adam Musial-Bright
Adam Musial-Bright

Reputation: 351

Core Data insert validation (validateForInsert) fires too early?

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

Answers (2)

Jerry Krinock
Jerry Krinock

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

Sachin Vas
Sachin Vas

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

Related Questions