RestKit serialize objects shortcut - iphone

is there a shorter way to do this?
iam fetching all core data objects with some predicate and then parsing them to JSON, using RestKit and defined mapping... i still want to use RestKit, but cant it be done more simple?
- (NSArray *)serializedManagedObjectClass:(Class)managedObjectClass updatedSinceRevision:(long long)revision {
NSArray * managedObjects = [managedObjectClass MR_findAllSortedBy:#"createdDate" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"revision > %lld", revision]];
NSError * error = nil;
NSMutableArray * serializedObjects = [[NSMutableArray alloc] initWithCapacity:[managedObjects count]];
for (NSManagedObject * managedObject in managedObjects) {
id object = [NSMutableDictionary new];
RKObjectMappingOperation * op = [RKObjectMappingOperation mappingOperationFromObject:managedObject toObject:object withMapping:[self.objectManager.mappingProvider serializationMappingForClass:managedObjectClass]];
BOOL success = [op performMapping:&error];
if ((success) && (error == nil) && (object)) {
[serializedObjects addObject:object];
} else {
ALog(#"Serialization problem with %#, possible error %#", managedObject, error);
}
}
return serializedObjects;
}

You don't need to map each object individually. As long as you have set the serialization mapping for that class, RestKit will handle the serialization when you try to send instances of that object. It looks like you have already created and assigned the mapping for the class.
Now, just add your array of managed objects to your dictionary of parameters that are being sent to the server. RestKit should then map each object according to the mapping provided.

Related

Need to update nsmanagedobject from nsarray for loop - iphone

I have a coredata project that I'm trying to programmatically update a number.
I'm retrieving objects from CoreData and then storing it into an array.
Then, I'm looping through that array to see if the current user's IP is present in the database and trying to update the number of times accessed for that specific array.
The problem is, it's updating all the objects, not just the current object in the looped array.
First, I get the info from core data like so:
- (void)fetchRecords {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"IPAddr" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"ipDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setIpArray: mutableFetchResults];
}
Now, I'm trying to find if the current User IP is present in the fetched results, and if it's present, update the number of times accessed:
// see if the ip is present and update if necessary
-(void)ipPresent {
NSString * theCurrentIP = [self getGlobalIPAddress];
for (IPAddr *allips in ipArray)
{
if ([allips.ipNum isEqualToString:theCurrentIP]) {
NSLog(#"The IP %# was found.", theCurrentIP);
// update the ip
NSError *error = nil;
NSNumber *ipToUpdate = allips.ipAccess;
NSNumber *addIpAccess = [[NSNumber alloc] initWithInt:1];
NSNumber *updateIpAddress = [NSNumber numberWithFloat:([ipToUpdate floatValue] + [addIpAccess floatValue])];
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
if ([self.managedObjectContext save:&error]) { // write to database
NSLog(#"The IP Was Updated from %# to %#", ipToUpdate, updateIpAddress);
} else if (![self.managedObjectContext save:&error]) {
NSLog(#"failed with error: %#", error);
}
break;
} else {
NSLog(#"The IP %# was NOT found.", theCurrentIP);
}
}
}
I'm pretty sure the issue is with this line:
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
Again, it's updating ALL the entities and not just the one in the current loop.
Indeed. You are using the wrong method. self.ipArray is a NSMutableArray.
The method
- (void)setValue:(id)value forKey:(NSString *)key
is used for Key-Value Coding (which is what makes it work for Core Data objects), but when applied to an array, it will invoke setValue:forKey: on each entry in the array.
Now, you can see that you could also call setValue:forKey on the one single array element allips since its property is obviously KVC compliant -- otherwise you would be having a different problem, not see the values being set.
Note, that you could also just assign the property...
allips.ipAccess = updateIpAddress;
EDIT
Sorry, probably should have read slower... You do understand that you don't have to use a mutable array, right? You are not actually changing the array, just the elements in the array. An immutable collection means that the collection contents can not change, but when you have a pointer to an object, as long as that object is not immutable, you can still mutate its properties.
Thus, if you had an immutable array of Foo objects, you could do this...
for (Foo *foo in myImmutableArray) {
Bar *bar = [self getSomeNewBar];
[foo setBar:bar];
// If Foo is KVC compliant, you can do this too...
[foo setValue:bar for Key:#"bar"];
}
If, however, you call setValue:forKey on the array, it will be invoked for each element of the array. Note, that setValue:forKey is actually declared in the immutable NSArray.
EDIT
That comment was hard to read.
The core data object is just another object. It looks like you have subclassed it, and provided it with properties for the attributes. Just replace
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
with
[allips setValue:updateIpAddress forKey:#"ipAccess"];
or
allips.ipAccess = updateIpAddress;
Either of those should modify your core data object, as they would any object that had a read/write property named "ipAccess"
Assuming, of course, that I didn't read it wrong again... and allips is your core data object...

UIManagedDocument - Validating Core Data Entity

I have an app that uses Core Data and it gets its ManagedObjectContext by using UIManagedObject. From reading, I see that I am not suppose to save the context directly - rather I should depend on autosaving of UIManagedObject or use saveToURL:... My issue is that I want to validate the data being stored in my entity. I have constraints on the entity that specify that the min length for the string properties is 1. However, I can create a new object, assign its properties empty strings, and save the file. In the completion handler of saveToURL:... it always has a true success value. I then created my own validator for the name property of my entity. I used sample code from the Core Data Programming Guide -
-(BOOL)validateName:(id *)ioValue error:(__autoreleasing NSError **)outError
{
if (*ioValue == nil)
{
if (outError != NULL)
{
NSString *errorStr = #"nil error";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorStr
forKey:NSLocalizedDescriptionKey];
NSError __autoreleasing *error = [[NSError alloc] initWithDomain:#"domain"
code:1
userInfo:userInfoDict];
*outError = error;
}
return NO;
}
else if( [*ioValue length] == 0 )
{
if (outError != NULL) {
NSString *errorStr = #"length error";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorStr
forKey:NSLocalizedDescriptionKey];
NSError __autoreleasing *error = [[NSError alloc] initWithDomain:#"domain"
code:1
userInfo:userInfoDict];
*outError = error;
}
return NO;
}
else
{
return YES;
}
}
When this runs, I see that the ioValue has 0 length and that it returns NO, but then my completion handler is never called. Any help would be great.
Is there something I am missing for how to handle saving errors with UIManagedDocument - particularly how to notify the calling code that an error happened while saving its information.
As a rule, you should only call saveToURL to create a brand new file. Let auto-save do the rest.
Also, I'm not sure I follow your question. If you are asking how to know about save failures, the best you can do is register for notifications (since all saves happen on a background thread).
Directly from the documentation:
A UIDocument object has a specific state at any moment in its life cycle. You can check the current state by querying the documentState property. And you can be notified of changes in the state of a document by observing the UIDocumentStateChangedNotification notification.
I guess I need to implement handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted in a subclass of the UIManagedDocument. I found that via this article - http://blog.stevex.net/2011/12/uimanageddocument-autosave-troubleshooting/

how to get objects from a json array in iphone?

I am working on an iPhone app which involves using json-framework.I am getting array using NSURL
[{"firstName":"X","lastName":"Y","id":1},{"firstName":"A","lastName":"B","id":2}]
How can i get these 2 objects as like if i query for id=1, the O/P is
id=1
firstName:X
lastName:Y
and putting it in a table.
I am googling the stuff from many days but didn't get any solution.
Please help me out , explanation through code is appreciated.
Thank You.
If your target SDK is ios4 or higher, you can use this project
https://github.com/stig/json-framework/
Once you add the source to your project, just
#import "SBJson.h"
and convert your Json string as follows
jsonResponse = [string JSONValue];
The method will fail if you don't have the full Json array in your string, but you can keep appending strings until it doesn't fail
To follow up for codejunkie's request below
you can assume in your data structure that the jsonResponse is an NSArray
In other implementations take care to test the response for NSArray or NSDictionary
NSArray * myPeople = [string JSONValue];
NSMutableDictionary * organizedData = [[NSMutableDictionary alloc] init];
for (NSDictionary * p in myPeople) {
[organizedData setValue:p forKey:[p valueForKey:#"id"]];
}
// now you can query for an id like so
// [organizedData valueForKey:#"1"]; and your output will be what you wanted from the original question
// just don't forget to release organizedData when you are done with it
https://github.com/johnezang/JSONKit
I use this to get data from a webservice that spits out 50 records each having another 20 internal elements similar to the one you specify...
I use the JSONKit in the following manner..(Had a look at SBJson a lot of user but i got confused from the word go.)
JSONDecoder *jArray = [[JSONDecoder alloc]init];
NSMutableArray *theObject = [[NSMutableArray alloc] init];
theObject = [jArray objectWithData:theResponseData];//objectWithString:theResponseString
NSMutableArray *csArray = [[NSMutableArray array] retain] ;
for(id key in theObject)
{
if([key valueForKey:#"firstName"] != Nil)
{
........
}
if([key valueForKey:#"lastName"] != Nil)
{
........
}
}
check it out and let me know if it works or not.. By the way Great responses guys... Good

How can I loop through loading data from multiple entities in Core Data in the least amount of code?

I am trying to make loading multiple entities more manageable in code. The code below is attached to an object that can load over 1000 times, so I've put it in a loop and attached a manageable array of entities.
The problem is, because I have no idea if a key is available in singleObject, I am getting a crash on [singleObject valueForKey:#"actor"] when I am on the directors entity. It's because the key "actor" doesn't exist. See my code here.
NSArray *entities = [[NSArray alloc] initWithObjects: #"actors", #"directors", #"subtitles", #"audios", nil];
for (NSString *anEntity in entities)
{
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:anEntity inManagedObjectContext:context];
[request setEntity:entityDescription];
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
// error
}
for (NSManagedObject *singleObject in objects)
{
if (singleObject != nil)
{
if ([singleObject valueForKey:#"actor"] != nil)
{
[self.actors addObject:[singleObject valueForKey:#"actor"]];
}
else if ([singleObject valueForKey:#"director"] != nil)
{
[self.directors addObject:[singleObject valueForKey:#"director"]];
}
else if ([singleObject valueForKey:#"subtitle"] != nil)
{
[self.subtitles addObject:[singleObject valueForKey:#"subtitle"]];
}
else if ([singleObject valueForKey:#"audio"] != nil)
{
[self.audios addObject:[singleObject valueForKey:#"audio"]];
}
}
}
}
[entities release];
[request release];'
How can I make this code work, and be vastly expandable, without having to put a bunch of try/catches around everything?
OK I think I found a way that works for me using NSDictionary instead. It works in my situation simply because my entity names are plural (actors) and my attributes are singular (actor). Here is what I came up with:
NSMutableDictionary *entities = [[NSMutableDictionary alloc] init];
[entities setValue:actors forKey:#"actors"];
[entities setValue:directors forKey:#"directors"];
[entities setValue:subtitles forKey:#"subtitles"];
[entities setValue:audios forKey:#"audios"];
for (NSMutableString *aKey in entities)
{
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:aKey inManagedObjectContext:context];
[request setEntity:entityDescription];
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
// error
}
for (NSManagedObject *singleObject in objects)
{
if (singleObject != nil)
{
if ([singleObject valueForKey:[aKey substringToIndex:[aKey length] - 1]] != nil)
{
[[entities valueForKey:aKey] addObject:[singleObject valueForKey:[aKey substringToIndex:[aKey length] - 1]]];
}
}
}
}
Details:
I loop through the keys in the dictionary. I make them mutable so I can take the s off of the end of the key. Then I pull the value (which is an array) from that key and load the array using the key without the s.
Somewhat complicated to read the code, but really slims down the manageability. Now all I need to do is add a new plural entity with matching singular attribute and just add the key/value pair to the dictionary.
I don't know if this will help you or not, because I am not 100% clear on what you are trying to do, but I had a similar problem when importing data from a MySQL database into CoreData. I made extensive use of NSEntityDescription
's methods propertiesByName, attributesByName, and relationshipsByName. In my case, using the field descriptions from the MySQL table, I made sure that my model attributes matched the field names, so instead of needing to do a whole bunch ofif/else tests, I just had the code test to see if [[SomeEntityDescription attributesByName] valueForKey:someFieldName] returned something besides nil. If it did, I set the value for the object I was creating from the imported data using the field name as the key.
You can do something similar for your relationships. if I understand what you are trying to do correctly, by checking against [someEntityDescription relationshipsByName] keys, or, you can get all the relationships that target your entity of interest using relationshipsWithDestinationEntity:(NSEntityDescription *)entity.

Core Data - accessing objects returned from fetch EXC_BAD_ACCESS

The following is implemented as a class method in a category on one of my managed object entities, with a bunch of other operations:
+ (NSArray*)newArrayOfAllOtherBibs
{
NSManagedObjectContext* moc = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSString* entityName = NSStringFromClass([self class]);
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
NSError* error;
NSArray* items = [moc executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSMutableArray* bibArray = [NSMutableArray array];
for(int i = 0; i < [items count]; i++)
{
Person* person = [items objectAtIndex:i];
if(![person.isSelf isEqualToString:#"YES"])
{
// crash here!
if([person.bib length] > 0)
// crash here!
[bibArray addObject:person.bib];
}
}
return [bibArray retain];
}
So it is supposed to look at all entities for Person, returned for a very simple fetch, and add all that are not marked as "self" (attribute isSelf, type NSString) to a new array which is returned to the caller. All the other methods for add/delete/find matching are working well.
Using
NSString* entityName = NSStringFromClass([self class]);
is an attempt to make the function more generic so I can cut&paste these functions for other entities as required with less editing.
Person looks like this:
#interface Person : NSManagedObject
{
}
#property (nonatomic, retain) NSString * bib;
#property (nonatomic, retain) NSString * isSelf;
[...]
#end
Question is, what could explain EXC_BAD_ACCESS on the line marked? Analyzer shows nothing. At that point person is clearly OK or I would expect it to die earlier, on accessing .isSelf. (It could be made a boolean but I find having two kinds of boolean, one BOOL and one NSNumber, error prone). If person.bib is nil then calling length on it should still return 0 right? But it can't be nil anyway since "bib" is the key for these entities and one is never created without it. At this stage nothing has been deleted from the store, it's a fresh store that has been saved since last addition.
The cause of the problem:
NSDictionary *resultDict = [responseString JSONValue];
NSString* bib = [resultDict objectForKey:#"bib"];
person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:moc];
person.bib = bib;
[... lots of stuff setting up relationships for person etc.]
NSError *error;
if (![moc save:&error])
{
NSLog(#"Core Data Save error %#, %#", error, [error userInfo]);
}
The fix:
person.bib = [bib copy];
seems like the bib string was not valid at the time the Person entity was saved.
The best thing to do is set NSZombieEnabled in the executable and debug it from there. This way you'll be able to see what is causing the problem.
If you are loading some vars lazily make sure you set them to nil when you release them because if not next time when accessed them. They will not be "skipped", you app will try to use them but since they are not pointing to a valid object it fails and gives you errors like EXC_BAD_ACCESS
Person.bib may not have been loaded yet.
Did you leave the bib property defined as #dynamic and not #synthesize?
Did you override the bib accessor method? If so your override needs to be KVO friendly.