How to update a posted object in RestKit with different primary key attributes? - objective-c

I'm posting objects on a server as JSON that consists of a few attributes and an ID that is the primary key attribute. I'm also using Core Data to save all the objects locally.
The problem is that when I first create the object to POST I know all the attributes but the unique ID. The ID is set at server-side, and when I get the response from the server I have ended up with two objects in my database:
One with ID 0, and one with the real ID.
Is there any way to get restkit/coredata to treat these two objects as the same, or alternatively don't save the first object in the database?

You can use the postObject: usingBlock method and assign a target object for the object loader like this.
[[RKObjectManager sharedManager] postObject:myObject usingBlock:^(RKObjectLoader *loader) {
loader.targetObject = myObject;
loader.delegate = self;
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[myObject class]];
}
Note that you also have to set the delegate manually in the block of code every time you run the method.

Related

Objective c - Suggested process to prefill complex DB

i would like to have some clarification about the correct approach to prefill a (complex) DB with multiple entities.
This is the approach i want to use:
Creation of a separate project that cares about DB Population
In case of Entities with no relations: parsing of a .json file in order to gather information of the Entity
In case of Entities with relation with other Entities (1:N relation):
I have a .json file for each entity
I allocate a Managed Object for each element involved with the relation between the Entities and i fill their attributes.
Please consider the example below for better explaination:
Let's consider the DB contains two Entities: Owner and House with a (1:N) relation.
In order to prefill the DB I:
configurea owner.json file and a house.json file
parse owner.json and gather and create a ManagedOwnerObject
parse house.json and create a set of ManagedHouseObject that have a relation with the first object created.
fill the attribute of ManagedOwnerObject that describe the relation with ManagedHouseObject with the NSSet just created
Iterate the process for all the house.json
Now, this process seems to me a little complex considering that my application has something like 10 Entities connected with 1:N relation.
Could you please suggest me if i'm doing right or if other better solutions could be considered?
Kind regards
Nicolò
You could simply fill the database manually using your app and ship the resulting SQLite file with your app instead of creating an empty one when the user opens the app for the first time.
EDIT:
Using KVC could simplify the creation of the objects. Setting the relationships probably isn't that easy. I would go through all of the data twice. Create the object without relationships in the first run and set up the relationships in the second.
Here is a method to do something similar, without relationships though:
-(void)createEntities:(NSString *)entityName fromFile:(NSString *)filePath inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
// Remove all existing objects of this entity.
[self removeExistingEntriesForEntityName:entityName managedObjectContext:managedObjectContext];
// Read all data from file path.
NSArray *newObjects = [self readTestDataFromFilePath:filePath];
// Insert new object for all existing keys.
for (NSDictionary *no in newObjects) {
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
for (NSString *key in no.keyEnumerator) {
[managedObject setValue:[no valueForKey:key] forKeyPath:key];
}
}
[(AppDelegate *)[[UIApplication sharedApplication] delegate] saveContext];
}

RestKit Basic K-V Mapping

I have a JSON payload that is being returned from my server and is structured like this:
{"users":[users]}
Using a simple RKObjectMapping and RKResponseDescriptor, I'm able to successfully map this JSON into a collection of User objects when I use getObjectsAtPath, but now I'd like to make a slight modification to the JSON server-side. Namely, I'd like to add a key-path
{"users":[users], "more":true}
where the key-path "more" indicates whether there are more users to load that are not included in the "users" array. The problem I'm having is that I can't find a simple way to access the value of this "more" key-path. Ideally, I'd like to define a Mapping and Response Descriptor that map "more" into a BOOL (or NSNumber), but I'm having trouble figuring out how to do this. Adding a mapping like
RKObjectMapping *moreMapping = [RKObjectMapping mappingForClass:[NSNumber class]]
with ResponseDescriptor
[RKResponseDescriptor responseDescriptorWithMapping:moreMapping pathPattern:pathPattern
keyPath:#"more" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Doesn't do the trick. Ultimately, in the success block of getObjects, I'd like my RKMappingResult to be structured like so:
#{#"users":[users], #"more":1}
Any tips?
RestKit is based around mapping data into objects, not to objects in the way you're after. The closest you can get is to map the true into an NSNumber inside a dictionary or array. Try:
RKObjectMapping *moreMapping = [RKObjectMapping requestMapping];
[moreMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"more"]];

Retrieve Specific Object from Core Data

I'm new to Core Data and wondering if it is possible to get an object based on it's attributes, more specifically, an uniqueID I assigned to it. I'm trying to do this because I'm interfacing with a web server, which provides data that will updated the Core Data. I want to search through each of the web server objects, check the timestamp, and if it's different, retrieve that object from core data, and update. I've looked at using existingObjectWithId but it seems like I would have to know which object I'm searching for, or the ID of that object. I've also thought about sorting the data in both arrays, and then checking each simultaneously, but didn't think that is viable.
Here is what I'm doing so far:
-(NSMutableArray*) updateRootBeers:(NSMutableArray*)webRootBeerList{
NSManagedObjectContext* moc = [self managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc]initWithEntityName:#"Rootbeer"];
coreDataRootBeerList = [[moc executeFetchRequest:fetchRequest error:nil]mutableCopy];
//check to see if core data has data, if not, call function to populate the list
if (coreDataRootBeerList.count <=0) {
[self addRootBeerToCoreData:webRootBeerList];
}else{
//otherwise I want to update the core data object if object data is different.
for(RootBeer* currentRootBeer in webRootBeerList){
RootBeer* mRootBeer = [moc existingObjectWithId:currentRootBeer error:nil];
}
}
}
I've also thought about using nested for loops to check for the data in each array, but that seems like poor coding.
Any help or thoughts would be great.
You want to make an NSFetchRequest. You can set the entity and provide a Predicate. Really simple and clean.

NSManagedObjects with like properties

I have a UITableView that displays the value of a property called name of an NSManagedObject using CoreData. I have it working by just using a basic NSFetchRequest and then displaying the value of name in the UITableViewCell's textLabel.
However, many of the NSManagedObject's have the same name value, so I get duplicates in my table. How can I filter it so that I only have one of each name value?
Thanks for any help.
You can configure your fetch request to only return distinct values but that required that you return dictionaries instead of managed objects. Since you are asking for dictionaries you will have to specify what values to return.
You can see my answer to avoid duplicate results on Core Data fetch.
In short:
request.resultType = NSDictionaryResultType;
request.propertiesToFetch = [NSArray arrayWithObject:#"name"];
request.returnsDistinctResults = YES;

How to get the ID of an object saved to Core Data's managed object context?

I have this code:
NSEntityDescription *userEntity = [[[engine managedObjectModel] entitiesByName] objectForKey:#"User"];
User *user = [[User alloc] initWithEntity:userEntity insertIntoManagedObjectContext:[engine managedObjectContext]];
and I want to know the id of the object inserted to the managed object context. How can i get that?
Will that id remain the same for that object's lifetime or will it persist to the sqlLite database beneath this and be something that can be used to uniquely identify it during a fetch operation (my ultimate goal).
Any help appreciated // :)
If you want to save an object's ID permanently you need to:
Save the object into the context so that the ID changes from a temporary to a permanent ID.
Extract the URI version of the permanent ID with -[NSManagedObjectID URIRepresentation]. That returns a NSURL you can store as transformable attribute in another managed object.
You can get the object by using -[NSPersistentStoreCoordinator managedObjectIDForURIRepresentation:] to generate a new NSManagedObjectID object and then use -[NSManagedObjectContext objectWithID:] to get the actual referenced managed object.
The URI is supposed to identify a particular object in a particular store on a particular computer but it can change if you make any structural changes to the store such as migrating it to a new model version.
However, you probably don't need to do any of this. ObjectIDs play a much smaller role in Core Data than they do in other Data Model systems. Core Data maintains an object graph that uniquely identifies objects by their location in the graph. Simply walking the graph relationships takes you to a specific unique object.
The only time you really need ObjectID is when you're accessing object across two or more persistent stores. You need them then because relationships don't cross stores.
Read up on "managed object IDs" in the Core Data Programming Guide
You can get the object id from the object with something like:
NSManagedObjectID *moID = [managedObject objectID];
First, you are constructing your objects in a non-preferred manner. Generally you should:
User *user = [NSEntityDescription insertEntityForName:#"User" intoManagedObjectContext:[engine managedObjectContext]];
Second, when you create the object it will get a temporary id which you can access via [user objectID] as David mentioned. Once you save the context then it will get a new "permanent" id.
However this id can and does change over the lifetime of the entity (although not the instance). Things like migrating the data can cause this id to change. However, between saving the context and exiting the application the id will remain the same.