Restkit creating NULL core data objects - restkit

I am building an app with Restkit to sync up with a web service. The first loadObjectsAtResourcePath call works properly, but then each additional call creates a new a row of all NULLs. A typical JSON response is:
[{"item":{"created_at":"2012-04-19T19:42:44Z","id":4,"json_event_id":null,"name":"Biz Cards","theme_id":null,"updated_at":"2012-04-19T19:42:44Z","url":"","user_id":3}},{"item":{"created_at":"2012-04-20T19:37:33Z","id":5,"json_event_id":null,"name":"Hummus","theme_id":null,"updated_at":"2012-04-20T19:37:33Z","url":"","user_id":3}}]
The first time I call loadObjectsAtResourcePath, I correctly get the two objects created in core data. The next time I call it, I get an additional row in Sqlite with a new PK and all NULLs. Each subsequent call creates a new NULL row with the PK incremented.
Here is my mapping:
RKManagedObjectMapping *itemsMapping = [RKManagedObjectMapping mappingForClass:[Items class] inManagedObjectStore:objectManager.objectStore];
itemsMapping.primaryKeyAttribute = #"itemID";
[itemsMapping mapKeyPathsToAttributes:#"created_at", #"createdAt",
#"id", #"itemID",
#"json_event_id", #"json_event_id",
#"name", #"name",
#"theme_id", #"theme_id",
#"updated_at", #"updatedAt",
#"url", #"url",
#"user_id", #"user_id", nil];
And here is my call to the web service:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager loadObjectsAtResourcePath:#"/items" delegate:self];
Any help on why this is creating those additional rows would be greatly appreciated!

Related

SWIFT OS X - deleting managed objects from persistent store: fatal error

I am using the function below to delete managed objects from a persistent store.
The deletion function is:
func deletePPRRowDataManagedObject(date: NSDate) {
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(self.pprRowEntity, inManagedObjectContext: self.managedObjectContext!) as! PPRRowData
var pprRowDataArray = self.fetchPPRRowDataManagedObjects()
let filteredPPRRowDataArray = pprRowDataArray.filter({$0.pprDate == date})
for object in filteredPPRRowDataArray {
managedObjectContext?.deleteObject(object)
}
var pprFileDataArray = self.fetchPPRFileDataManagedObjects()
let filteredPPRFileDataArray = pprFileDataArray.filter({$0.pprDate == date})
for object in filteredPPRFileDataArray {
managedObjectContext?.deleteObject(object)
}
var error: NSError? = nil
if !managedObjectContext!.save(&error) {
// this code needs to be replaced to properly deal with the error
abort()
}
}
I have three subclassed Core Data entities of which I am trying to delete managed objects from the persistent store for two them in this code.
There are no relationships defined between the three entities. All three are saved within the same managed object context and the same persistent store.
After I have deleted the managed objects I can create and fetch data from the persistent store without a runtime error. I can access data from the third entity but now get a data fault when i try to access the data arrays created from the fetch..... functions that source the managed objects from the persistent store for the two entities that I have deleted some managed objects for.
The code (below) to fetch the data from the persistent store runs without error. The error
(EXC_BAD_ACCESS (code=1, address=0x0) error)
occurs when I try to access the outputArray.
let outputArray = addDataFiles.fetchPPRRowDataManagedObjects()
When I run the code prior to any deletions it runs without any problems.
Any thoughts/guidance on what may be creating the error would be appreciated. I have not been able to determine if it is a problem with the code I am using to delete the managed objects or the relevant fetch... function that I am using.
The following is an example of one of the fetch... functions that I am using:
func fetchPPRRowDataManagedObjects() ->Array<PPRRowData>{
let fetchRequestPPRRowData = NSFetchRequest()
let entityPPRRowData = NSEntityDescription.entityForName(self.pprRowEntity, inManagedObjectContext: self.managedObjectContext!)
var resultsArrayPPRRowData: Array<PPRRowData> = []
var arrayFetchResults: Array<AnyObject> = []
fetchRequestPPRRowData.entity = entityPPRRowData
var error: NSError? = nil
arrayFetchResults = managedObjectContext!.executeFetchRequest(fetchRequestPPRRowData, error: &error)!
for index in arrayFetchResults {
resultsArrayPPRRowData.append(index as! PPRRowData)
}
return resultsArrayPPRRowData
}
Ok - after a lot of code testing, I have identified the problem was an erroneous !managedObjectContext!.save() statement that was firing within the loop as I was iterating through the code - both in the createManagedObject() function and in the .deleteManagedObject() function that I had created. Not even sure why it was there (that is, why I had put it there in the first place) but it was obviously creating the problem.
Marcus, many thanks for your patience and suggestions, it led to me reviewing my code more closely and eventually identifying the problem. – Wolfstar 1 hour ago

Coredata NSFetchRequest DictionaryResultType null properties Swift

Hello guys i am using this code to fetch the Result from Coredata
func getRequest(entiryDesc:NSEntityDescription) -> NSFetchRequest{
var request:NSFetchRequest = NSFetchRequest()
request.entity = entiryDesc
request.resultType = NSFetchRequestResultType.DictionaryResultType
return request
}
Now the problem is i need All the attributes which contains the Nil value too but the excutefetchrequest returns only those properties which have values , is there any work around for this to return Null attributes with String like "" every time i fetch ? Thanks Advanced
Of course, you can just dispense with the .DictionaryResultType and fetch normal managed objects. There are very few cases where the dictionary result type makes sense.
If you want to construct a dictionary with all attributes filled out (for whatever opaque reason), remember two things:
Make sure to insert the null values as objects NSNull()
You can use the NSEntityDescription API to generate all the attribute keys. Use entityDescription.propertiesByName.allKeys to generate a list of all attribute names of your entity.

CloudKit. Problems with subscriptions

I do not quite understand the information about subscriptions. I have Private Database. There is a table to which I create a subscription. If I subscribe to two devices under one iCloud account, errors occur: Subscribed with error:
CKError 0x165c6ac0: "Server Rejected Request" (15/2032); server message = "subscription is duplicate of 'C6051A24-2DB5-47EA-98D2-7D8786AA6D3B'"; uuid = 48AC7472-006A-4FD7-84A9-DB342C83C199; container ID = "iCloud.com. *. *"
NSPredicate *truePredicate = [NSPredicate predicateWithValue:YES];
CKSubscription *itemSubscription = [[CKSubscription alloc] initWithRecordType:RecordType
predicate:truePredicate
options:CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordUpdate];
CKNotificationInfo *notification = [[CKNotificationInfo alloc] init];
notification.shouldSendContentAvailable = YES;
notification.alertLocalizationKey = #"";
notification.shouldBadge = NO;
itemSubscription.notificationInfo = notification;
[self.privateDatabase saveSubscription:itemSubscription completionHandler
Maybe I'm doing something wrong? What I need to do to both devices receive notification of subscriptions?
Edited:
Also. I create only one subscription. If I use method fetchAllSubscriptionsWithCompletionhandler I see 3-4 of the subscriptions per table. But it's weird because I create only one. And when I receive the notification, I see more notifications than it should be.
A subscription is per Database. So if you want to receive notifications to multiple users on a database it is enough to create it once. You are getting an error because you are creating the same subscription (same subscription ID) twice on the same database. Simply make sure you create it only once. You can fetch subscriptions to check if it has already been created or not. Harry

PFUser refreshWithKey?

So I have my currentUser. There are some pointers in my user class that aren't included on first load. At some point later I need them.
I understand that I can just get the pointer from currentUser and then query for that object. However, I want that key INCLUDED/ATTACHED to the currentUser object so I can use it at this time and at later times.
I don't see any methods in the Parse docs for this. The only way I can imagine doing this is if I just run a separate user query, includeKey for the pointers I want to include, and then save this retrieved object as currentUser.
HOWEVER, I'm doing this a lot in my app and I'm wondering if there is some convenience method in the Parse API that I'm missing - something like a refreshWithKey method.
If you have pointers in your user class that are not available, you can use fetchIfNeeded on them:
PFObject *address = [PFUser currentUser][#"address"];
[address fetchIfNeeded];
Or with a block:
[address fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
NSString *street = address[#"street"];
}];

NSManagedObjects not being saved -- Beware of Inverse Relationships

(Answer found. See below.)
In the following code, I am updating about 350,000 records by linking some of the relationships. However, in the end, I examine the sqlite database, and only a fraction of the relationships are saved. The rest are still nil.
-update-
Before describing the code in question, I should explain that the dictionaryOfSynsetDictionaries contains prefetched synset objects. It is organized as a dictionary containing four dictionaries, where the keys are 'n', 'v', 'a', and 'r' (for the four parts of speech, or pos). Each of the inner dictionaries contains references to the synset objects, which are subclassed from NSManagedObject.
Each inner dictionaries is keyed by what is called synsetOffset.
The code below fetches all of the SYNSET_POINTER objects from the store and puts them into an array. Each SYNSET_POINER object refers to a synset by the synsetOffset and partOfSpeech (pos) properties. It also contains a relationship that links the SYNSET_POINTER object to the synset by matching synsetOffset and pos to the corresponding synset in dictionaryOfSynsetDictionaries
So now, after having prefetched and organized the synset objects into the dictionary, the following code fetches all of the SYNSET_POINTER objects into an array, iterates over the array, linking synset pointers to their synset objects directly through the corresponding relationship.
(The picture above shows two relationships between the SYNSET and SYNSET_POINTER objects. This is based on the original dataset organization. They serve two different purposes. For this question, I am referring to the one-to-one relationship.)
-end update-
Here is the code doing the updates:
[request setEntity:[NSEntityDescription entityForName:#"SYNSET_POINTER" inManagedObjectContext:[ManagedObjectContext moc]]];
predicate = nil;
[request setPredicate:predicate];
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"synsetOffset" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSArray *synsetPointersArray = [[ManagedObjectContext moc] executeFetchRequest:request error:&error];
int i = 0;
int j = 0;
for(SYNSET_POINTER *pointer in synsetPointersArray) {
NSString *pos = pointer.partOfSpeech;
NSString *offset = [pointer.synsetOffset stringValue];
pointer.synsetPointer = [[dictionaryOfSynsetDictionaries objectForKey:pos] objectForKey:offset];
error = nil;
if (![[ManagedObjectContext moc] save:&error]) {
NSLog(#"error with save\n%#\n%#",error.localizedFailureReason, error.localizedDescription);
NSLog(#"pause and quit");
}
NSLog(#"pos %#, offset %#, pointer %#", pos, offset, pointer);
if (j==100) {
NSLog(#"%# %d", pos, i);
j=0;
}
i++;
j++;
}
Here, I am updating the property synsetPointer for the pointer of class SYNSET_POINTER, a subclass of NSManagedObject. I can see that in each iteration of the loop, the synsetPointer relationship does point to a proper object, as in this debugger output:
2012-11-26 22:03:08.753 [26156:fb03] pos v, offset 5815, pointer <SYNSET_POINTER: 0x404e84f0> (entity: SYNSET_POINTER; id: 0x404d00d0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET_POINTER/p282133> ; data: {
partOfSpeech = v;
pointerSymbol = "~";
reverseRelatedSynset = "0x244cd880 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p85476>";
sourceTarget = 0000;
synsetOffset = 5815;
synsetPointer = "0x244cdda0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p83738>";
})
2012-11-26 22:03:08.822 [26156:fb03] pos v, offset 5815, pointer <SYNSET_POINTER: 0x404e8530> (entity: SYNSET_POINTER; id: 0x404d00e0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET_POINTER/p285862> ; data: {
partOfSpeech = v;
pointerSymbol = "#";
reverseRelatedSynset = "0x244cd870 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p86470>";
sourceTarget = 0000;
synsetOffset = 5815;
synsetPointer = "0x244cdda0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p83738>";
})
The synsetPointersArray is sorted by the synsetOffset value. I can sort the table in the sqlite viewer in Firefox, and I see that most of the values remain nil. The debug output above shows they are all assigned. For some reason, they are not being saved.
Can anyone see a problem with this code that would prevent some of the updates?
Solved!
It wasn't obvious from all of the information I provided, but I discovered that the one-to-one relationship shown in the picture was the problem.
SYNSET_POINTER.synsetPointer should point to one and only one SYNSET object.
But it tuns out that more than one SYNSET_POINTER object can point to an individual SYNSET object.
Therefore, the reverse relationship, SYNSET.reverseSynsetPoint, should have been configured as part of a one-to-many bidirectional relationship.
Since the managed object context takes care of resolving reverse relationships, the one-to-one relationship implies that previously assigned links were being nullified automatically. Assinging a new SYNSET_POINTER to a SYNSET that already has a SYNSET_POINTER-to-SYNSET relationship set will nullify the existing link in both directions every time the SYNSET.reverseSynSetPointer was changed. This would have left a dangling link on the SYNSET_POINTER side, but the managed object context nullifies it automatically.
So Core Data had been setting and saving the changes. But after many of these links were configured, as I expected, they also were being nullified.
In my case, I really didn't need the reverse relationship, and included it as an afterthought, thinking I might use it at a later time. My advice is to think carefully about these types of reverse relationships when they are initially set up. Otherwise, they may create trouble later.
Your code uses very poor variable names which makes it difficult to understand what you want to achieve. However, it seems you are making a fundamental conceptional mistake.
In general, to establish relationships in core data, you do not save references to other Core Data objects as attributes in those objects. Rather, you use relationships and let core data handle the technicalities of foreign keys. In order to add an object to a relationship, use the Core Data generated accessors in your object classes.
Check out these sections of the Core Data Programming Guide:
Relationships and Fetched Properties
Managed Objects

Resources