Problem creating NSManagedObject derived class - objective-c

I am doing something wrong here... I know that
I'm using Xcode and I have created the following class using the data modeller:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Project : NSManagedObject {
#private
}
#property (nonatomic, retain) NSNumber * indent;
#property (nonatomic, retain) NSNumber * collapsed;
#property (nonatomic, retain) NSString * color;
#property (nonatomic, retain) NSNumber * project_id;
#property (nonatomic, retain) NSNumber * item_order;
#property (nonatomic, retain) NSNumber * cache_count;
#property (nonatomic, retain) NSNumber * user_id;
#property (nonatomic, retain) NSString * name;
#end
When I am trying to propagate this class with data from a JSON source using the following code:
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"projects" ofType:#"json"];
if (filePath) {
NSString* jsonString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
DLog(#"JSON for Projects:%#", jsonString);
SBJsonParser* jsonParser = [SBJsonParser new];
id response = [jsonParser objectWithString:jsonString];
NSArray* array = (NSArray*) response;
NSEnumerator* e = [array objectEnumerator];
NSDictionary* dictionary;
while ((dictionary = (NSDictionary*)[e nextObject])) {
Project* project = [[Project alloc] init];
project.user_id = [dictionary objectForKey:#"user_id"];
project.name = [dictionary objectForKey:#"name"];
project.color = [dictionary objectForKey:#"color"];
project.collapsed = [dictionary objectForKey:#"collapsed"];
project.item_order = [dictionary objectForKey:#"item_order"];
project.cache_count = [dictionary objectForKey:#"cache_count"];
project.indent = [dictionary objectForKey:#"indent"];
project.project_id = [dictionary objectForKey:#"project_id"];
[elementArray addObject:project];
[project release];
}
}
However, the code stops at the project.user_id = [dictionary objectForKey:#"user_id"]; line with an exception "* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Project setUser_id:]: unrecognized selector sent to instance 0x590bcb0'"
I don't know why this is happening or how to resolve this.

I've set up a reality distortion field so I don't violate my NDA. And now I can answer your question, it has nothing to do with the product-that-must-not-be-named anyway.
There is your bug: Project* project = [[Project alloc] init];
The #dynamic setters and getters are not created for you if you create your object this way.
You can't use NSManagedObjects without a NSManagedObjectContext.
You should use something like this:
Project *project = (Project *)[NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:self.managedObjectContext];

Property names with underscores are not very sensible in the Objective C world - I guess the properties generated by Core Data have the wrong names therefore. Try using CamelCase, that is calling your properties userID, itemOrder, cacheCount etc.

You may need to set up your getters and setters.
It could be as simple as adding:
#synthesize user_id;
In your class file.

Related

NSMutableArray can not add object?

I have 2 model:
#interface Program : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, strong) NSMutableArray *guides;
#end
#interface Guide : NSObject
#property (nonatomic, retain) NSString *name;
#end
And I add some guides to program from one xml:
Program *program = [Program new];
program.name = #"My list"
for(DDXMLElement *guideElement in [programElement nodesForXPath:#"guide" error:&error])
{
Guide *guide = [Guide new];
guide.name = [guideElement stringValue];// [p attribute:#"name"];
[program.guides addObject:guide];
NSLog(#"load guide number: %d", [program.guides count]);
}
The out is always "load guide number: 0"
program.guides is nil, since you never created it.
In your Program's init method, add:
self.guides = [[NSMutableArray alloc] init];
Or, more sloppily, before your for loop add:
program.guides = [[NSMutableArray alloc] init];

SBJsonParser can't parse NSArray

I am trying to parse NSArray to JSON but I get the following error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0xa93e460'
* First throw call stack: (0x21f1012 0x1feae7e 0x227c4bd 0x21e0bbc 0x21e094e 0x3445a 0x33ecc 0x26a453f 0x26b6014 0x26a72e8 0x26a7450
0x95e22e12 0x95e0acca) libc++abi.dylib: terminate called throwing an
exception
I have included all classes from SBJson_3.1.1/Classes directory.
This is code:
NSMutableArray* arr = ...get array
NSString* jsonArr = [arr JSONRepresentation]; // here I get error
When I do this in array of simple strings it works:
NSData *jsonData = [NSJSONSerialization arr
options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
But my array contain list of objects (Person) maybe there is a problem.
I use Item instead of person just as example
Item.h
#interface Item : NSObject
{
BOOL IsOpen;
NSString* Description;
}
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property NSString* Description;
- (id) proxyForJson;
#end
Item.m
#implementation Item
#synthesize ItemId;
#synthesize SequenceId;
#synthesize Description;
#synthesize IsOpen;
- (id) proxyForJson {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:#"%i", ItemId], #"ItemId",
SequenceId, #"SequenceId",
Description, #"Description",
IsScanned, #"IsOpen",
nil ];
}
#end
UPDATE
Student example
I tried to make a separate project. I copied to new project all from classes directory of sbjson framework. This is code:
#import "SBJson.h"
#interface Student : NSObject
{
NSString *name;
NSInteger sid;
NSString *email;
}
#property NSString *name;
#property NSInteger sid;
#property NSString *email;
- (id) proxyForJson;
#end
#implementation Student
#synthesize name;
#synthesize sid;
#synthesize email;
- (id) proxyForJson{
return [NSDictionary dictionaryWithObjectsAndKeys:
name, #"student_name",
[NSNumber numberWithInt:sid], #"student_id",
email, #"email",
nil ];
}
#end
NSMutableArray* studentArray = [[NSMutableArray alloc]init];
Student* s1 = [[Student alloc]init];
s1.name = #"student 1";
s1.sid = 45;
s1.email = #"test#test.com";
Student* s2 = [[Student alloc]init];
s2.name = #"student 2";
s2.sid = 46;
s2.email = #"plavi#test.com";
[studentArray addObject:s1];
[studentArray addObject:s2];
NSString *jsonString = [studentArray JSONRepresentation];
NSLog(#"%#", jsonString);
And again I get error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0x741b100'
SBJson doesn't support serialising user-defined classes without assistance. If you implement a -proxyForJson method in your Person class (example here) it should work, however.
If you're using a recent Xcode the below should work. Header:
#interface Item : NSObject
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property(copy) NSString* Description;
- (id) proxyForJson;
#end
Implementation:
#implementation Item
- (id) proxyForJson {
return #{ #"ItemId": #(self.ItemId),
#"SequenceId": #(self.SequenceId),
#"Description": self.Description,
#"IsOpen": #(self.IsOpen)
};
}
#end
This should let SBJson serialise the Item objects to NSDictionaries. However, SBJson does not support parsing JSON into custom objects. So you will always get this back in the dictionary form. I don't know of any Objective-C JSON parser that provides bindings to custom types.
I would suggest reading the top two comments of this thread. If those don't help, it is still very likely that you are not installing the library correctly. Try removing the SBJSON files from your project and then readding them, making sure that they are added to your target. Also, make sure you are importing the SBJSON header into your class.
I would suggest that you try using JSONRepresentation on an array of NSString objects. If the framework is correctly installed, this should definitely work. This way you can narrow down whether it is an installation issue or whether it is an issue with your custom class.
Check out the following excerpt from Working with JSON in iOS 5 Tutorial
This is mainly for generating JSON.
//build an info object and convert to json
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
[loan objectForKey:#"name"],
#"who",
[(NSDictionary*)[loan objectForKey:#"location"]
objectForKey:#"country"],
#"where",
[NSNumber numberWithFloat: outstandingAmount],
#"what",nil];
//convert object to data
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:info options:NSJSONWritingPrettyPrinted error:&error];
Now, the difference lies in using NSDictionary and converting that into JSON Data. Try forming the JSON in the way given above and check if the problem persists.
you are correctly linking the category? to me it kinda looks like you are missing a category

RestKit save object manually

I had spent few hours try to insert/add object but duplicated records happened. Campaign record re-inserted even it already exists. Am I missing something?
Below are my codes:
Campaign.h
#interface Campaign : NSManagedObject
#property (nonatomic, strong) NSNumber* campaignId;
#property (nonatomic, strong) NSString* title;
#end
Card.h
#class Campaign;
#interface Card : NSManagedObject
#property (nonatomic, strong) NSNumber* cardId;
#property (nonatomic, strong) NSString* name;
#property (nonatomic, strong) Campaign* campaign;
#end
ViewController.m
...
Campaign* campaign = [Campaign object];
campaign.campaignId = [NSNumber numberWithInt:1];
campaign.title = #"Hello world";
Card* card = [Card object];
card.cardId = #"1234567890";
card.campaign = campaign;
[[[RKObjectManager sharedManager] objectStore] save:nil];
EDITED ViewController.m
...
RKManagedObjectMapping* cardMapping = [RKManagedObjectMapping mappingForClass:[Card class] inManagedObjectStore:[RKObjectManager sharedManager].objectStore];
[cardMapping mapKeyPath:#"id" toAttribute:#"cardId"];
[campaignMapping mapKeyPath:#"name" toAttribute:#"name"];
cardMapping.primaryKeyAttribute = #"cardId";
RKManagedObjectMapping* campaignMapping = [RKManagedObjectMapping mappingForClass:[Campaign class] inManagedObjectStore:[RKObjectManager sharedManager].objectStore];
[campaignMapping mapKeyPath:#"id" toAttribute:#"campaignId"];
[campaignMapping mapKeyPath:#"title" toAttribute:#"title"];
campaignMapping.primaryKeyAttribute = #"campaignId";
Campaign* campaign = [Campaign object];
campaign.campaignId = [NSNumber numberWithInt:1];
campaign.title = #"Hello world";
Card* card = [Card object];
card.cardId = #"1234567890";
card.campaign = campaign;
[[[RKObjectManager sharedManager] objectStore] save:nil];
Yes, add a .primaryKeyAttribute to your mapping.
This will do the pk stuff for you when you are importing data via rest kit. If you are just doing 'normal' core data stuff with Restkit, you need to deal with referential integtrity etc. yourself.

Objective C Inheritence

I am trying to subclass some Core Data classes. I've got the following classes:
Core Data class:
#interface CDExplanatoryMaterial : NSManagedObject
#property (nonatomic, retain) NSString * document;
#property (nonatomic, retain) NSString * id;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * pageNumber;
#property (nonatomic, retain) NSNumber * realPageNumber;
#property (nonatomic, retain) NSString * url;
#end
Business logic class's protocol:
#protocol BLDataClass <NSObject>
- (NSArray*)favouriteInGroups;
#property (nonatomic, readonly, retain) NSString* type;
#property (nonatomic, readonly, retain) NSArray* inFavouriteGroups;
- (void)addAddToFavouriteGroup: (NSString*) groupName;
- (void)removeFromFavouriteGroup: (NSString*) groupName;
- (void)addToHistory;
#end
Interface for BLExplanatoryMaterial:
#interface BLExplanatoryMaterial : CDExplanatoryMaterial <BLDataClass>
I get the data like this:
+ (NSMutableArray*) explanatoryMaterials {
NSMutableArray* results = [[NSMutableArray alloc] init];
for(CDExplanatoryMaterial *item in [Helper fetchDataObjectsOfType:#"CDExplanatoryMaterial"])
{
[results addObject: (BLExplanatoryMaterial*)item];
}
return results;
}
The helper class looks like this:
#implementation Helper
+ (NSArray*) fetchDataObjectsOfType:(NSString *)type {
DataManager* manager = [DataManager sharedInstance];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:type inManagedObjectContext:manager.mainObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [manager.mainObjectContext executeFetchRequest:fetchRequest error:&error];
return fetchedObjects;
}
#end
This issue that I have is that the fetchedObjects array in fetchDataObjectsOfType and the results array in explanatoryMaterials only contain NSManagedObject objects. I'd expect fetchedObjects to contain CDExplanatoryMaterial objects and results to contain BLExplanatoryMaterial. I need the end result to be BLExplanatoryMaterial objects or I can't use any of my instance methods, what is the best way to do this?
Thanks,
Joe
EDIT:
Just to clarify it is the following code that fails because expMat is an NSManagedObject and doesn't support the addToFavouriteGroup method.
NSMutableArray* expMats = [Data explanatoryMaterials];
BLExplanatoryMaterial* expMat = (BLExplanatoryMaterial*) [expMats objectAtIndex:0];
[((BLExplanatoryMaterial*)expMat) addToFavouriteGroup:#"Test Group"]
One thing that I forgot to mention is that all of the code in my original post is in a static library. The code posted in this edit is in a IOS App project. I'm not sure if this makes a difference. All of the classes in the static library are marked as public.
You need to specify BLExplanatoryMaterial as the class for the entity in your managed object model. This will tell Core Data to instantiate objects of that class instead of NSManagedObject.
Thanks for all of the help. It turns out that because the core data classes were in a seperate static library they were not being built in to the main bundle. To get around this I subclassed them within my main application and then pointed the core data file at those subclasses.

Does this copy class method leak memory?

- (id)copyWithZone:(NSZone *)zone {
PoolFacility *copy = [[[self class] allocWithZone:zone]init];
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
//make sure I get proper copies of my dictionaries
copy.address = [self.address mutableCopy];
copy.webAddress = [self.webAddress copy];
copy.prices = [self.prices mutableCopy];
copy.pools = [self.pools mutableCopy];
return copy;
}
Can anyone see any memory leaks?
Here's the property types:
NSString *name;
NSString *type;
NSMutableDictionary *address;
NSString *phoneNumber;
NSString *webAddress;
NSMutableArray *prices;
NSMutableArray *pools;
Here are the property declarations:
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *type;
#property (nonatomic, copy) NSString *phoneNumber;
#property (nonatomic, retain) NSMutableDictionary *address;
#property (nonatomic, copy) NSString *webAddress;
#property (nonatomic, retain) NSMutableArray *prices;
#property (nonatomic, retain) NSMutableArray *pools;
The properties defined as copy and not retain will have an extra copy when set as below (your code)
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
copy.webAddress = [self.webAddress copy];
it should be sufficient to only write them as
copy.name = self.name;
copy.type = self.type;
copy.phoneNumber = self.phoneNumber;
copy.webAddress = self.webAddress;
This almost certainly leaks like a sieve. You need to provide your #property and other method declarations for us to recommend the best way to fix it.