NSNumber changes value when adding it to NSManagedObject - core-data

NSDictionary* ordersTableRow = arrayOfDictionaries[i];
NSMutableDictionary* ordersTemp = [[NSMutableDictionary alloc] initWithDictionary:ordersTableRow];
[ordersTemp removeObjectForKey:#"_id"];
NSNumber *myCHICNumber = [ordersTableRow objectForKey:#"chicnumber"];
NSLog(#"myCHICNumber = %#", myCHICNumber);
[ordersTemp removeObjectForKey:#"chicnumber"];
[ordersTemp setObject:myCHICNumber forKey:#"chicnumber"];
[ordersTemp setObject:[ordersTableRow objectForKey:#"_id"] forKey:#"rowindex"];
NSLog(#"chicnumber for OrderDetails = %#", [ordersTableRow objectForKey:#"chicnumber"]);
[ordersRecord setValuesForKeysWithDictionary:ordersTemp];
NSLog(#"chicnumber for OrderDetails = %#", [ordersTableRow objectForKey:#"chicnumber"]);
NSLog(#"chicnumber for OrdersRecord = %#", [ordersRecord valueForKey:#"chicnumber"]);
[ordersRecord setValue:myCHICNumber forKey:#"chicnumber"];
NSLog(#"chicnumber for OrdersRecord = %#", [ordersRecord valueForKey:#"chicnumber"]);
Yields :
2014-03-27 09:20:57.227 [2269:3107] myCHICNumber = 2013037
2014-03-27 09:20:57.227 [2269:3107] chicnumber for OrderDetails = 2013037
2014-03-27 09:21:08.243 [2269:3107] chicnumber for OrderDetails = 2013037
2014-03-27 09:21:13.283 [2269:3107] chicnumber for OrdersRecord = -18579
2014-03-27 09:21:47.459 [2269:3107] chicnumber for OrdersRecord = -18579
NSManagedObject defined :
#property (nonatomic, retain) NSNumber * orderid;
#property (nonatomic, retain) NSString * productid;
#property (nonatomic, retain) NSNumber * quantity;
#property (nonatomic, retain) NSString * colourcode;
#property (nonatomic, retain) NSString * notes;
#property (nonatomic, retain) NSNumber * chicnumber;
#property (nonatomic, retain) NSString * colourname;
#property (nonatomic, retain) NSNumber * rowindex;
So : as soon as i put the NSNumber chicnumber into the NSManagedObject, whether by setValuesForKeysWithDictionary, or by setting the value specifically with setValue:forKey: the NSNumber changes from 2013037 to -18579.

It looks like chicnumber is configured as "Integer 16" in the Core Data model. But you're assigning a value of 2013037, which requires more than 16 bits. When you do this, the value is corrupted and you end up with an incorrect value.
Use a larger integer type in the data model. This sample value would fit in a 32 bit integer. Whether you need 64 bits depends on what other values you might get for chicnumber.

Related

CoreData: Error by decode data from Context/Store NSData -> NSArray

i'm new in iOS-Development area and trying now to update an existing App to iOS 6.1 from iOS4.
get Data from Store to show in NewsPageView : UIViewController
NewsPageView.m
#import "NewsPageView.h"
#interface NewsPageView ()
#end
#implementation NewsPageView
#synthesize newsDetailsView,
newsTableView,
newsHeaderText
;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
/*
* Get managedObjectContext and List of News over AppDelegate
*/
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
//Setting self.managedObjectContext
self.managedObjectContext = appDelegate.managedObjectContext;
// Fetching Records from the data base to show in the table
self.listOfNews = [[appDelegate getDataFromStore:self]copy];
//
NSLog(#"Data from Store in NewsPageView: %#",self.listOfNews.description);
}
try to set/write data into Context
-(void) setDataToStore
{
NSLog(#"Trying to write News in Store");
News *newNews = [NSEntityDescription insertNewObjectForEntityForName:#"News" inManagedObjectContext:self.managedObjectContext];
newNews.newsId = [[NSNumber alloc]initWithInt:508];
newNews.title = #"Some Title";
newNews.content = #"Some content";
newNews.detailsUrl = #"www.someURL.de";
newNews.date = [[NSDate alloc]init];
newNews.dateTs = [[NSNumber alloc]initWithInt:138114787];
newNews.primaryCat = [[NSNumber alloc]initWithInt:0];
//make Categorien...
NewsCategorien *cat1 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat1.catId = [[NSNumber alloc]initWithInt:1];
cat1.name = #"Windows";
cat1.news = newNews;
NewsCategorien *cat2 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat2.catId = [[NSNumber alloc]initWithInt:2];
cat2.name = #"Linux";
cat2.news = newNews;
NewsCategorien *cat3 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat3.catId = [[NSNumber alloc]initWithInt:3];
cat3.name = #"Apple";
cat3.news = newNews;
NewsCategorien *cat5 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat5.catId = [[NSNumber alloc]initWithInt:5];
cat5.name = #"Smartphone";
cat5.news = newNews;
newNews.isNews = [NSNumber numberWithBool:YES];
NSArray *catListe = [NSArray arrayWithObjects:cat1,cat2,cat3,cat5, nil];
// //saving cat by converting Cat. objects
// newNews.categories = catListe;
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Saving ok!");
}
try to get Data from Context
- (NSArray *)getDataFromStore:(id)sender
{
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSArray *fetchedData;
//get Data für NewsTable View
if([sender isKindOfClass:[NewsPageView class]]){
//Setting Entity to be Queried
fetchRequest.entity = [NSEntityDescription entityForName:#"News" inManagedObjectContext:self.managedObjectContext];
NSError* error;
//Point of Error by debugging!!!
//return list of News
fetchedData = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
}
//print fetched Data
for (News *news in fetchedData) {
NSLog(#"News:%#", news.description);
}
// Returning Fetched News
return fetchedData;
}
Classes
News.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class NewsCategorien;
#interface News : NSManagedObject <NSCoding>
#property (nonatomic, retain) id categories;
#property (nonatomic, retain) NSString * content;
#property (nonatomic, retain) NSDate * date;
#property (nonatomic, retain) NSNumber * dateTs;
#property (nonatomic, retain) NSString * detailsUrl;
#property (nonatomic, retain) NSNumber * isNews;
#property (nonatomic, retain) NSNumber * newsId;
#property (nonatomic, retain) NSNumber * primaryCat;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSSet *categorie;
//have to be implement...
//to work with NSKeyedArchiver by serialising Objects
- (void)encodeWithCoder:(NSCoder *)aCoder;
//to work with NSKeyedUnarchiver by deserialising Objects
- (id)initWithCoder:(NSCoder *)aDecoder;
#end
#interface News (CoreDataGeneratedAccessors)
- (void)addCategorieObject:(NewsCategorien *)value;
- (void)removeCategorieObject:(NewsCategorien *)value;
- (void)addCategorie:(NSSet *)values;
- (void)removeCategorie:(NSSet *)values;
#end
/*
* News Transformer NSArray -> NSData and reverse
* Model: News should have Attribut type of Transformable and have name NewsTrasformer
*/
#interface NewsTransformer : NSValueTransformer
#end
News.m
#import "News.h"
#import "NewsCategorien.h"
#implementation News
#dynamic categories;
#dynamic content;
#dynamic date;
#dynamic dateTs;
#dynamic detailsUrl;
#dynamic isNews;
#dynamic newsId;
#dynamic primaryCat;
#dynamic title;
#dynamic categorie;
//tell the archiver how to transform the serialized representation into a new object -> deserialize Objects!
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.categories = [decoder decodeObjectForKey:#"categories"];
self.content = [decoder decodeObjectForKey:#"content"];
self.date = [decoder decodeObjectForKey:#"date"];
self.dateTs = [decoder decodeObjectForKey:#"dateTs"];
self.detailsUrl = [decoder decodeObjectForKey:#"detailsUrl"];
self.isNews = [decoder decodeObjectForKey:#"isNews"];
self.newsId = [decoder decodeObjectForKey:#"newsId"];
self.primaryCat = [decoder decodeObjectForKey:#"primaryCat"];
self.title = [decoder decodeObjectForKey:#"title"];
self.categorie = [decoder decodeObjectForKey:#"categorie"];
}
NSLog(#"<<<< ---- Deserialize Object News (Init With Code) --->");
return self;
}
//tell the archiver how to serialize your object into bytes -> serialize Objects!
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.categories forKey:#"categories"];
[encoder encodeObject:self.content forKey:#"content"];
[encoder encodeObject:self.date forKey:#"date"];
[encoder encodeObject:self.dateTs forKey:#"dateTs"];
[encoder encodeObject:self.detailsUrl forKey:#"detailsUrl"];
[encoder encodeObject:self.isNews forKey:#"isNews"];
[encoder encodeObject:self.newsId forKey:#"newsId"];
[encoder encodeObject:self.primaryCat forKey:#"primaryCat"];
[encoder encodeObject:self.title forKey:#"title"];
[encoder encodeObject:self.categorie forKey:#"categorie"];
NSLog(#"<<<< ---- Serialize Object News (encode With Coder) --->");
}
#end
//
#implementation NewsTransformer
//Return the Class for Forwarding Transformation
+ (Class)transformedValueClass
{
return [NSArray class];
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
// converts the NSArray into NSData using NSKeyedArchiver
- (id)transformedValue:(id)value
{
NSLog(#"Transformer: NSArray -> NSData");
return [NSKeyedArchiver archivedDataWithRootObject:value];
}
// by default raises an exception if +allowsReverseTransformation returns NO and otherwise invokes transformedValue:
//converts NSData to an NSArray using NSKeyedUnarchiver
- (id)reverseTransformedValue:(id)value
{
NSLog(#"Transformer: NSData -> NSArray");
return [NSKeyedUnarchiver unarchiveObjectWithData:value];
}
#end
NewsCategorien.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class News;
#interface NewsCategorien : NSManagedObject <NSCoding>
#property (nonatomic, retain) NSNumber * catId;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) News *news;
//for serializing of objects have to be implement...
- (void)encodeWithCoder:(NSCoder *)aCoder;
//for serializing of objects have to be implement...
- (id)initWithCoder:(NSCoder *)aDecoder;
#end
NewsCategorien.m
#import "NewsCategorien.h"
#import "News.h"
#implementation NewsCategorien
#dynamic catId;
#dynamic name;
#dynamic news;
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.catId = [decoder decodeObjectForKey:#"catId"];
self.name = [decoder decodeObjectForKey:#"name"];
self.news = [decoder decodeObjectForKey:#"news"];
}
NSLog(#"<<<< ---- Deserialize Object Categorie ---> : %#",self.description);
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.catId forKey:#"catId"];
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.news forKey:#"news"];
NSLog(#"<<<< ---- Serialize Object Categorie ---> %#",self.description);
}
#end
Model
News
Attribute: categorien Type: Transformable Name: NewsTransformer
Relationchip one-to-more
categorien Des. NewsCategorien Inverse news
Categorien
Att. catId String
Att. name Interger16
My problem:
1. writing data to the context - OK
2.when i'm trying to get data from the context(NSData->NSArray) i get following error:
Error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NewsCategorien'
2013-11-20 16:03:19.204 securityNews[19458:c07] -[NewsCategorien setCatId:]: unrecognized selector sent to instance 0x8173740
2013-11-20 16:03:19.205 securityNews[19458:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NewsCategorien setCatId:]: unrecognized selector sent to instance 0x8173740'
any idea what i doing wrong??
The first error is almost certainly due to your implementation of initWithCoder:, specifically this line:
if (self = [super init]) {
You can't create a managed object using init. You must use the designated initializer initWithEntity:insertIntoManagedObjectContext:. Or you can use NSEntityDescription's method insertNewObjectForEntityForName:inManagedObjectContext:. But using init is right out, and doing so will cause a Failed to call designated initializer error.
This means that your initWithCoder needs to be able to find a managed object context. How you do that depends on your app's structure and where you are creating contexts. If your app delegate has a context object and that's the one you want to use, you could look it up from there. If it's some other context, you'll have to get it from somewhere else. You have a couple of versions of initWithCoder and they both have this problem.
The second error is a side-effect of the first error. In initWithCoder you do this:
if (self = [super init]) {
self.catId = [decoder decodeObjectForKey:#"catId"];
....
But since [super init] doesn't work, you're getting back a bogus object that doesn't know what catId is. Then you try to set catId and it fails. Fix the first error and the second error will disappear.

NSManagedObject's #synthesize attribute stays faulted after modifying a relationship

I'm using code derived from Apple's DateSectionTitles example code. In my class Appointment I have a relationship to Location. Furthermore I generate a section identifier used by a UITableViewController.
#class Location;
#interface Appointment : NSManagedObject
#property (nonatomic, retain) NSDate * begin;
#property (nonatomic, retain) NSDate * end;
#property (nonatomic, retain) Location * location;
#property (nonatomic, retain) NSString *sectionIdentifier;
#property (nonatomic, retain) NSString *primitiveSectionIdentifier;
#end
#implementation Appointment
#synthesize begin = _begin;
#dynamic end;
#dynamic location;
#dynamic primitiveSectionIdentifier;
#dynamic sectionIdentifier;
#pragma mark -
#pragma mark Transient properties
- (NSString *)sectionIdentifier {
// Create and cache the section identifier on demand.
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp) {
/*
Sections are organized by month and year. Create the section identifier as a string representing the number (year * 1000) + month; this way they will be correctly ordered chronologically regardless of the actual name of the month.
*/
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate:[self begin]];
tmp = [NSString stringWithFormat:#"%d", ([components year] * 10000) + [components month] * 100 + [components day]];
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
#pragma mark -
#pragma mark Begin setter
- (void)setBegin:(NSDate *)begin
{
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"begin"];
[self willChangeValueForKey:#"primitiveSectionIdentifier"];
_begin = begin;
[self setPrimitiveSectionIdentifier:nil];
[self didChangeValueForKey:#"begin"];
[self didChangeValueForKey:#"primitiveSectionIdentifier"];
}
#end
The problem: After altering the location the data stays faulted.
Before modifying location the object looks like this:
<Appointment: 0x837d570> (entity: Appointment; id: 0x837c900 <x-coredata://83B2187C-00B3-4029-B4C5-4EB69C18FC59/Appointment/p1> ; data: {
begin = "2013-07-27 16:00:00 +0000";
end = "2013-07-27 18:00:00 +0000";
location = "0x837e6c0 <x-coredata://83B2187C-00B3-4029-B4C5-4EB69C18FC59/Location/p1>";
})
After altering the property location:
<Appointment: 0x9b7b1f0> (entity: Appointment; id: 0x9b7ab50 <x-coredata://83B2187C-00B3-4029-B4C5-4EB69C18FC59/Appointment/p1> ; data: <fault>)
If I relinquish from generating a section identifier and using a #dynamic instead of a #synthesized property it still works. What's the cause of this and how can I overcome this?
Thanks to Martin R who pointed me in the right direction I've found the problem in my code.
What I didn't know was the fact that Core Data automatically generates additional primitive properties for you:
For example, given an entity with an attribute firstName, Core Data
automatically generates firstName, setFirstName:, primitiveFirstName,
and setPrimitiveFirstName:. Core Data does this even for entities
represented by NSManagedObject. To suppress compiler warnings when you
invoke these methods, you should use the Objective-C 2.0 declared
properties feature, as described in “Declaration.”
(Source)
The fact that I wasn't aware of this leaded me to adopt the example code in a wrong way. The way it works for me is now:
#class Location;
#interface Appointment : NSManagedObject
#property (nonatomic, retain) NSDate * primitiveBegin;
#property (nonatomic, retain) NSDate * begin;
#property (nonatomic, retain) NSDate * end;
#property (nonatomic, retain) Location * location;
#property (nonatomic, retain) NSString *sectionIdentifier;
#property (nonatomic, retain) NSString *primitiveSectionIdentifier;
#end
#implementation Appointment
#dynamic primitiveBegin;
#dynamic begin;
#dynamic end;
#dynamic location;
#dynamic primitiveSectionIdentifier;
#dynamic sectionIdentifier;
#pragma mark -
#pragma mark Transient properties
- (NSString *)sectionIdentifier {
// Create and cache the section identifier on demand.
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp) {
/*
Sections are organized by month and year. Create the section identifier as a string representing the number (year * 1000) + month; this way they will be correctly ordered chronologically regardless of the actual name of the month.
*/
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate:[self begin]];
tmp = [NSString stringWithFormat:#"%d", ([components year] * 10000) + [components month] * 100 + [components day]];
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
#pragma mark -
#pragma mark Begin setter
- (void)setBegin:(NSDate *)begin
{
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"begin"];
[self setPrimitiveBegin:begin];
[self didChangeValueForKey:#"begin"];
[self setPrimitiveSectionIdentifier:nil];
}
#pragma mark -
#pragma mark Key path dependencies
+ (NSSet *)keyPathsForValuesAffectingSectionIdentifier
{
// If the value of timeStamp changes, the section identifier may change as well.
return [NSSet setWithObject:#"begin"];
}
#end

CoreData: error: Failed to call designated initializer on NSManagedObject class while using NSCoding

I keep getting this error when run my app: "CoreData: error: Failed to call designated initializer on NSManagedObject class "Ambulance"". I've seen this problem on stack overflow already but im unsure of how to implement a solution in my case. Any ideas?
Ambulance.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Ambulance : NSManagedObject
#property (nonatomic, retain) NSString * ambulanceID;
#property (nonatomic, retain) NSNumber * distance;
#property (nonatomic, retain) NSNumber * etaDistance;
#property (nonatomic, retain) NSString * emsID;
#property (nonatomic, retain) NSString * lat;
#property (nonatomic, retain) NSString * lng;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * occupancy;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * websocketServer;
#property (nonatomic, retain) NSNumber * seconds;
#property (nonatomic, retain) id eta;
+(Ambulance *)nearestAmbulanceByType:(NSString *)type;
- (id)initWithCoder:(NSCoder *)decoder;
- (void)encodeWithCoder:(NSCoder *)encoder;
#end
Ambulance.m
#import "Ambulance.h"
#implementation Ambulance
#synthesize ambulanceID;
#synthesize distance;
#synthesize etaDistance;
#synthesize emsID;
#synthesize lat;
#synthesize lng;
#synthesize name;
#synthesize occupancy;
#synthesize type;
#synthesize websocketServer;
#synthesize seconds;
#synthesize eta;
+(Ambulance *)nearestAmbulanceByType:(NSString *)type {
NSArray *sortedArray = [Ambulance findByAttribute:#"type" withValue:type andOrderBy:#"distance" ascending:YES];
return [sortedArray first];
}
- (void)encodeWithCoder:(NSCoder *)encoder {
//Encode properties, other class variables, etc
[encoder encodeObject:self.ambulanceID forKey:#"ambulanceID"];
[encoder encodeObject:self.distance forKey:#"distance"];
[encoder encodeObject:self.etaDistance forKey:#"etaDistance"];
[encoder encodeObject:self.emsID forKey:#"emsID"];
[encoder encodeObject:self.lat forKey:#"lat"];
[encoder encodeObject:self.lng forKey:#"lng"];
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.occupancy forKey:#"occupancy"];
[encoder encodeObject:self.type forKey:#"type"];
[encoder encodeObject:self.websocketServer:#"websocketServer"];
[encoder encodeObject:self.seconds forKey:#"seconds"];
[encoder encodeObject:self.eta forKey:#"eta"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.ambulanceID = [decoder decodeObjectForKey:#"ambulanceID"];
self.distance = [decoder decodeObjectForKey:#"distance"];
self.etaDistance = [decoder decodeObjectForKey:#"etaDistance"];
self.emsID = [decoder decodeObjectForKey:#"emsID"];
self.lat = [decoder decodeObjectForKey:#"lat"];
self.lng = [decoder decodeObjectForKey:#"lng"];
self.name = [decoder decodeObjectForKey:#"name"];
self.occupancy = [decoder decodeObjectForKey:#"occupancy"];
self.type = [decoder decodeObjectForKey:#"type"];
self.websocketServer = [decoder decodeObjectForKey:#"websocketServer"];
self.seconds = [decoder decodeObjectForKey:#"seconds"];
self.eta = [decoder decodeObjectForKey:#"eta"];
}
return self;
}
You should use any other initializer than initWithEntity:insertIntoManagedObjectContext: for your NSManagedObject subclass.
From the docs:
Important: This method is the designated initializer for
NSManagedObject. You must not initialize a managed object simply by
sending it init.
Are you sure you have to use NSCoding with your subclass? It would probably be sufficient to store the objectID since your object exists in a NSManagedObjectContext and probably gets persisted anyways.
If you really rely on NSCoding you have to determine and feed the proper NSManagedObjectContext to initWithEntity:insertIntoManagedObjectContext: in initWithCoder:.
Not sure what this has to do with RestKit and why you think you need to support coding.
Assuming you are using RestKit though, you should be able to change to:
- (id)initWithCoder:(NSCoder *)decoder {
NSManagedObjectContext *moc = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Ambulance" inManagedObjectContext:moc];
if((self = [super initWithEntity:entity insertIntoManagedObjectContext:moc])) {
...
If you aren't using RestKit then you'll need a different way to get the MOC.

Restkit 0.20 pre4 core data issue

{"status":"ok",
"count":5,
"count_total":165,
"pages":33,
"posts":[{ "id":2971,
"status":"publish",
"title":"title1",
"content":"",
"date":"date",
"categories":[{"id":5,
"title":"category1"},
{"id":7,
"title":"category2"}],
"thumbnail":"url",
"custom_fields":{"wpcf-content":["content"],
"wpcf-audio":["url"]}
},
{ "id":2974,
"status":"publish",
"title":"title2",
"content":"",
"date":"date2",
"categories":[{"id":5,
"title":"category1"},
{"id":5,
"title":"category3"}
],
"thumbnail":"url",
"custom_fields":{"wpcf-content":["content"],
"wpcf-audio":["url"]}
}
]
}
posts.h
#interface posts : NSManagedObject
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) NSArray* wpcf-content;
#property (nonatomic, retain) NSArray* wpcf-audio;
#property (nonatomic, retain) NSString* id;
#property (nonatomic, retain) NSArray* categories;
#end
categories.h
#interface categories : NSManagedObject
#property (nonatomic, retain) NSString* title;
#end
Model.xcdatamodeld
entities Posts : title (string), id (integer 64),wpcf-audio (transformable),wpcf-content (transformable)
Categories : title (string)
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"Model.sqlite"];
[managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:nil];
[managedObjectStore createManagedObjectContexts];
RKEntityMapping *categoryMapping = [RKEntityMapping mappingForEntityForName:#"Categories" inManagedObjectStore:managedObjectStore];
[categoryMapping addAttributeMappingsFromDictionary:#{ #"title":#"title"}];
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Posts" inManagedObjectStore:managedObjectStore];
NSArray *identification =[[NSArray alloc]initWithObjects:#"id", nil];
mapping.identificationAttributes= identification;
[mapping addAttributeMappingsFromArray:#[#"title",#"id",#"custom_fields.wpcf-content",#"custom_fields.wpcf-audio"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:#"posts" statusCodes:statusCodes];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"categories" toKeyPath:#"categories" withMapping:categoryMapping]];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://my.url/json"]];
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
operation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
posts *article = [result firstObject];
categories *catego =[article.categories objectAtIndex:0];
NSLog(#"Mapped the article: %#", article.title);
NSLog(#"Mapped the article: %#", catego.title);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://my.url/"]];
[manager enqueueObjectRequestOperation:operation];
I get this error W restkit.core_data:RKManagedObjectMappingOperationDataSource.m:157 Performing managed object mapping with a nil managed object cache:
Unable to update existing object instances by identification attributes. Duplicate objects may be created.

Core data storing link to parent record only on last child record stored

I've got a core data model created using XCode 4 that is doing something weird. I have an entity called ProbeObj that has a defined relationship with a second entity called SmokeObj. In the diagram, I've created the relationship on ProbObj as ProbeToSmoke and on SmokeObj, I have created the relationship as SmokeToProbe and set it as an inverse relationship to the one defined on ProbeObj. I generated the .h & .m files for these two objects and I include them in the class I created to handle the inserting of the data. Here they are:
//
// SmokeObj.h
// Stoker Monitor
//
// Created by NCGrimbo on 6/3/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class BlowerObj, CurBlowerObj, CurProbeObj, ProbeObj, StokerObj;
#interface SmokeObj : NSManagedObject {
#private
}
#property (nonatomic, retain) NSDate * dtEnd;
#property (nonatomic, retain) NSDate * dtStart;
#property (nonatomic, retain) StokerObj * SmokeToStoker;
#property (nonatomic, retain) ProbeObj * SmokeToProbe;
#property (nonatomic, retain) CurProbeObj * SmokeToCurProbe;
#property (nonatomic, retain) CurBlowerObj * SmokeToCurBlower;
#property (nonatomic, retain) BlowerObj * SmokeToBlower;
#end
//
// SmokeObj.m
// Stoker Monitor
//
// Created by NCGrimbo on 6/3/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "SmokeObj.h"
#import "BlowerObj.h"
#import "CurBlowerObj.h"
#import "CurProbeObj.h"
#import "ProbeObj.h"
#implementation SmokeObj
#dynamic dtEnd;
#dynamic dtStart;
#dynamic SmokeToStoker;
#dynamic SmokeToProbe;
#dynamic SmokeToCurProbe;
#dynamic SmokeToCurBlower;
#dynamic SmokeToBlower;
#end
//
// ProbeObj.h
// Stoker Monitor
//
// Created by NCGrimbo on 6/3/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class SmokeObj;
#interface ProbeObj : NSManagedObject {
#private
}
#property (nonatomic, retain) NSDate * dtDataRead;
#property (nonatomic, retain) NSNumber * fCurTemp;
#property (nonatomic, retain) NSNumber * fHighTemp;
#property (nonatomic, retain) NSNumber * fLowTemp;
#property (nonatomic, retain) NSNumber * fTargetTemp;
#property (nonatomic, retain) NSNumber * nAlarmType;
#property (nonatomic, retain) NSNumber * nCtrlAlrmType;
#property (nonatomic, retain) NSString * sBlower;
#property (nonatomic, retain) NSString * sID;
#property (nonatomic, retain) NSString * sUserAssignedName;
#property (nonatomic, retain) SmokeObj * ProbeToSmoke;
#end
//
// ProbeObj.m
// Stoker Monitor
//
// Created by NCGrimbo on 6/3/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "ProbeObj.h"
#implementation ProbeObj
#dynamic dtDataRead;
#dynamic fCurTemp;
#dynamic fHighTemp;
#dynamic fLowTemp;
#dynamic fTargetTemp;
#dynamic nAlarmType;
#dynamic nCtrlAlrmType;
#dynamic sBlower;
#dynamic sID;
#dynamic sUserAssignedName;
#dynamic ProbeToSmoke;
#end
My code gathers data from a website, parses it and then adds records to the ProbObj table. The data for the SmokeObj is passed into this part of the program and stored as self.SmokeForThisRun. Here's the code that handles this with irrelevant sections removed out:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSError *error;
NSArray *chunks = [responseString componentsSeparatedByString: #"{"];
[responseString release];
NSArray *details;
NSArray *arrTheData;
Boolean bIsSensor = false;
Boolean bIsBlower = false;
Boolean bStokerFound = false;
NSRange textRange;
for (int i = 0; i < [chunks count]; i++){
if(!bStokerFound){
}
else{
if ((!bIsBlower) && (!bIsSensor)){
}
else{
if (bIsSensor){
//Set the probe data that is fixed
ProbeObj *newProbe = [NSEntityDescription insertNewObjectForEntityForName:#"ProbeObj" inManagedObjectContext:[Common managedObjectContext]];
newProbe.dtDataRead = [NSDate date];
//newProbe.nSmokeID = [NSNumber numberWithInt: 0];
NSLog(#"Setting ProbeToSmoke = %#", self.SmokeForThisRun);
newProbe.ProbeToSmoke = self.SmokeForThisRun;
//Parse the sensor information then save the data {Parse code removed.}
//Save the Probe Record
if (![[Common managedObjectContext] save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
UIAlertView *saveError = [[UIAlertView alloc]initWithTitle:#"Error Saving the Probe Data." message:[error localizedDescription] delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[saveError show];
[saveError release];
}
else{
NSLog(#"Data saved for %#", newProbe);
}
}
}
}
}
}
This code gets called every 1 minute to get the latest information from the web site.
The problem I'm having is that when I look at the data either via code or a SQLLite database viewer app, the only record that has the SmokeObj set is the last record. For example, if SmokeObj = 1 and I collect ProbeObj data 3 times, only the last ProbeObj will have SmokeObj = 1. The other two will have a blank value for SmokeObj. If I only collect 1 ProbeObj, then it will have the correct SmokeObj value. The problems happens whether I collect data 1 time or 100 times. Only the last record collected has the correct SmokeObj.
Any help will be greatly appreciated.
-NCGrimbo
Your SmokeObj.SmokeToProbe relationship is set one-to-one. This means that any single instance of SmokeObj can have a relationship with only one other single instance of ProbeObj.
So, as you go through the loop, you first assign the SmokeObj in self.SmokeForThisRun to the first created ProbObj referenced by newProb. Then the next time through the loop you change the relationship to the second instance now held by newProb and so on. Only the last created instance of referenced by newProb actually ends up with the value for the relationship set.
The solution is to change the SmokeObj.SmokeToProbe to a to-many relationship so that the same SmokeObj obj can relate to several 'ProbeObj`.
(BTW, you should follow the naming conventions for entity/classes and properties. Entities and class names have the first letter capitialized but properties do not. E.g. SmokeObj.SmokeToProbe should be SmokeObj.smokeToProbe. It makes your code easier for others to read. )

Resources