I have a simple Person entity in Core Data, I noticed that when adding data, each row is auto generated a unique PK called objectID
I wish to retrieve the records using the objectID but I am getting the following error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'keypath objectID not found in
entity < NSSQLEntity Person id=2 >'
func fetchPerson(withID personID: Int,
context: NSManagedObjectContext,
completion: #escaping ([Person]?) -> Void) {
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
let predicate = NSPredicate(format: "\(#keyPath(Person.objectID)) == \(personID)")
fetchRequest.predicate = predicate
//perform aynchronous operation:
context.perform {
do {
let persons = try fetchRequest.execute()
//success - return array of persons
completion(persons)
} catch {
//error - return nil
completion(nil)
}
}
}
Calling the above :
//fetch person with specific id:
let personID: Int = 2
fetchPerson(withID: personID, context: persistentContainer.viewContext) { (persons: [Person]?) in
if persons != nil {
print("Fetching person with personID: \(personID)")
for person in persons! {
print("Person: \(person.objectID) - \(person.firstName!) \(person.lastName!)")
}
}
}
You can fetch with objectID by using the predicate "self == %#", objectID. An objectID is property of a managedObject and its type is NSManagedObjectID not an Int. In general it is better to create and manage your own objectId. I generally use UUID strings.
The objectID is a property of NSManagedObject but is not the name of a field in the persistent store or part of the entity description. You can't use it in predicates because it's not there to use as a filter. There is a field in the primary store that corresponds to the object ID, but it's also not available to fetch requests because that's not how Core Data is intended to be used.
If you want a primary key with Core Data, you need to create it yourself. Or if you really want SQL-style coding, use SQLite directly and don't get Core Data involved.
Related
I'm trying to save strings I have in my string array into the Core Data. My .xcdatamodel looks like this:
My saving function (a method of a class called "Memory"):
func save(from: [String])
{
for i in 0..<from.count
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let saved = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context)
saved.setValue(from[i], forKey: "password")
do
{
try context.save()
print("SAVED")
}
catch
{
print("ERROR - COULDN'T SAVE ", to)
}
}
print("NEW ", to, ": ")
print(save)
}
Lastly, inside my ViewController:
Memory().save(from: codes)
However, what I get is this:
Thread 1: Fatal error: Unresolved error Error
Domain=NSCocoaErrorDomain Code=134140 "Persistent store migration
failed, missing mapping model."
UserInfo={sourceModel=() isEditable
1, entities {
Person = "() name Person,
managedObjectClassName NSManagedObject, renamingIdentifier Person,
isAbstract 0, superentity name (null), properties {\n password =
\"(), name password, isOptional
1, isTransient 0, entity
You have made changes to your data model but you failed/forgot to migrate the model. If you don't have anything valuable in your current persistence store (SQLite database?) then I suggest you throw it away and let Core Data create a new one using the new model.
Otherwise you might want to fetch the previous version of your model from your source repository if you have one and do a proper migration. Here is a SO question of interest and Apple's documentation on migration
I will be super thankful for any help. How can I save instances to a join table without a unique identifier as a predicate? Can I use the managed object id to check if the item exists already?
I'm building an app with different exercise plans. Each plan holds many exercise, and an exercise can belong to many plans. I have structured my data model to include a custom join table so that I can query the completion status of an exercise from within one plan.
I'm sourcing my data from a json file and would like to save it to core data. I'm able to correctly save my CoreExercise, and CorePlan tables, however am having difficulty understanding how to save the instance of the object in the intermediate join table, since I'm unsure of what predicate to use.
I've written a class function to check if the instance exists, and to save it if it doesn't.
class CoreExercisePlan: NSManagedObject {
class func coreExercisesForExercisePlan(exerciseInfo: Exercise, planName: String, inManagedObjectContext context: NSManagedObjectContext) -> CoreExercisePlan? {
let request = NSFetchRequest(entityName: "CoreExercisePlan")
request.predicate = NSPredicate() // Search for ObjectID here? / How?
if let exercisePlan = (try? context.executeFetchRequest(request))?.first as? CoreExercisePlan {
print("we have this exercise plan already saved")
return exercisePlan
} else if let exercisePlan = NSEntityDescription.insertNewObjectForEntityForName("CoreExercisePlan", inManagedObjectContext: context) as? CoreExercisePlan {
exercisePlan.status = 0
exercisePlan.progress = 0
print("we are creating new object")
return exercisePlan
}
return nil
}
private func updateDatabaseWithExercisePlans(){
managedObjectContext?.performBlock {
// Array of exercises for each plan:
let coffeePlanExercises = self.coffeeExercises
let subwayPlanExercises = self.subwayExercises
for exercise in coffeePlanExercises {
_ = CoreExercisePlan.coreExercisesForExercisePlan(exercise, planName: "coffee", inManagedObjectContext: self.managedObjectContext!)
}
for exercise in subwayPlanExercises {
_ = CoreExercisePlan.coreExercisesForExercisePlan(exercise, planName: "subway", inManagedObjectContext: self.managedObjectContext!)
}
do {
try self.managedObjectContext?.save()
} catch let error {
print("printing error here: \(error)")
}
}
}
Is there a way to get the objectID of the instance in the join table, and use that as a predicate? Thanks!
I am working on an app where the user is connected (in) multiple school classes. Since a student will be in more than one class, am I able to set an array of pointers to an individual user or is that not possible (maybe a relation is better)?
Here is my code:
let classPointerQuery = PFQuery(className: "Classes")
classPointerQuery.whereKey("class_name", equalTo: self.classNameTextField.text!)
let classQuery = PFQuery.orQueryWithSubqueries([classPointerQuery])
classQuery.findObjectsInBackgroundWithBlock({ (results: [PFObject]?, error: NSError?) -> Void in
if let objects = results {
for object in objects {
let userInfo = PFUser.currentUser()!
userInfo["my_classes"] = object
userInfo.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error != nil {
spinningActivity.hideAnimated(true)
self.displayAlert("Error", message: error!.localizedDescription)
} else if success {
spinningActivity.hideAnimated(true)
self.dismissViewControllerAnimated(true, completion: nil)
} else {
spinningActivity.hideAnimated(true)
self.displayAlert("Something Went Wrong", message: "Please try again")
}
})
}
}
})
* Note: I also tried changing - userInfo["my_classes"] = object - to - userInfo["my_classes"] = [object] - and got an error, "invalid type for key my_classes, expected *Classes, but got array (Code: 111, Version: 1.12.0)"
What I am doing here is querying for the object of the class that I want - lets say the user wants to add the class "Physics" - the query queries for the "class_name" in the Parse class "Classes" and spits out the object. This object is then set the current user's "my_classes" -> a pointer object. I there a way, when the user wants to add "Calculus" that the pointer object in Parse will have 2 pointers instead of replacing the current pointer?
Thanks in advance for the help!
you cant store Pointers in array, you can store objectID in the array as string and do the query like that.... the general rule is that u use Pointers for 1:many relationships in database and Relations in many:many...
Update 1 - Saving objectID to Array in Parse
PFUser.currentUser()!.addObject(somePFObject.objectID!, forKey: "my_classes")
for queries you will than use containedIn
querySetup.whereKey("class", containedIn: array)
I have an existing Realm project with two models
Model1 -> id, updateDate, details
Model2 -> id, updateDate, title, model1
(yes I used a class object instead of the id - aargh. Both id are primary keys)
With an updated version of my app, I am adding a new property to Model1 (title) and changing Model2.model1 from type Model1 to type string (=Model1.id)
I wrote a migration block for this as per the samples provided
let migrationBlock: (RLMMigration, UInt64) -> Void = { (migration, oldSchemeVersion) in
if oldSchemeVersion < 1 {
migration.enumerateObjects(Model1.className()) { oldObject, newObject in
//Nothing needed, the title can be a blank
}
migration.enumerateObjects(Model2.className()) { oldObject, newObject in
if let oldModel1 = oldObject!["model1"] as? RLMDynamicObject {
newObject!["model1"] = oldModel1["id"]
}
}
}
}
let config = RLMRealmConfiguration.defaultConfiguration()
config.schemaVersion = newSchemaVersion
config.migrationBlock = migrationBlock
RLMRealmConfiguration.setDefaultConfiguration(config)
But at the end of the migration, when I try to access the default realm (Realm.defaultRealm), it fails with this error
Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'id' has duplicate values after migration.'
I cannot figure out why this is going wrong and what I am supposed to do to make this work. Any help would be appreciated.
NOTE - my code uses the Realm objective-c code but in a Swift app
I have a list objects from coredata and then I get objectId from one of those objects:
let fetchedId = poi.objectID.URIRepresentation()
Now I need to get entity for this specific objectID.
And I tried something like:
let entityDescription = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext!);
let request = NSFetchRequest();
request.entity = entityDescription;
let predicate = NSPredicate(format: "objectID = %i", fetchedId);
request.predicate = predicate;
var error: NSError?;
var objects = managedObjectContext?.executeFetchRequest(request,
error: &error)
But I get error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath objectID not found in entity <NSSQLEntity Person id=4>'
You can't query arbitrary properties of the NSManagedObject with a predicate for a NSFetchRequest. This will only work for attributes that are defined in your entity.
NSManagedObjectContext has two ways to retrieve an object with an NSManagedObjectID. The first one raises an exception if the object does not exist in the context:
managedObjectContext.objectWithID(objectID)
The second will fail by returning nil:
var error: NSError?
if let object = managedObjectContext.existingObjectWithID(objectID, error: &error) {
// do something with it
}
else {
println("Can't find object \(error)")
}
If you have a URI instead of a NSManagedObjectID you have to turn it into a NSManagedObjectID first. The persistentStoreCoordinator is used for this:
let objectID = managedObjectContext.persistentStoreCoordinator!.managedObjectIDForURIRepresentation(uri)
What you get is not the object ID, but the URI. The object ID is a part of the URI. You can ask the persistent store coordinator for the object ID with
- managedObjectIDForURIRepresentation:. Having the object ID you can get the object from the context using for example -objectWithID:. But please look to the documentation, of this methods for some reasons.