Below is my setup:
- (RKManagedObjectStore *)setupCoreDataWithRESTKit
{
NSError * error;
NSURL * modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"App" ofType:#"momd"]];
NSManagedObjectModel * managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
self.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[self.managedObjectStore createPersistentStoreCoordinator];
NSArray * searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentPath = [searchPaths objectAtIndex:0];
NSPersistentStore * persistentStore = [self.managedObjectStore addSQLitePersistentStoreAtPath:[NSString stringWithFormat:#"%#/App%#.sqlite", documentPath, [TBSPersistence username]] fromSeedDatabaseAtPath:nil withConfiguration:nil options:[self optionsForSqliteStore] error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
NSLog(#"Path: %#", [NSString stringWithFormat:#"%#/App%#.sqlite", documentPath, [TBSPersistence username]]);
if(!persistentStore){
NSLog(#"Failed to add persistent store: %#", error);
}
[self.managedObjectStore createManagedObjectContexts];
return self.managedObjectStore;
}
POST Invite -- Invite has a to-many relationship with Activity::
-(void)sendInvite:(NSInteger)methodType
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.objectManager = [self getObjectManager];
self.objectManager.managedObjectStore = appDelegate.managedObjectStore;
RKEntityMapping *invitationMapping = [RKEntityMapping mappingForEntityForName:kInvite
inManagedObjectStore:self.objectManager.managedObjectStore];
RKEntityMapping *activityMapping = [RKEntityMapping mappingForEntityForName:kActivity
inManagedObjectStore:self.objectManager.managedObjectStore];
Invite *invitation;
invitationMapping = [RESTMappingProvider invitionPostMapping:invitationMapping];
invitationMapping.identificationAttributes = #[#"inviteId"];
activityMapping = [RESTMappingProvider activityPostMapping:activityMapping];
activityMapping.identificationAttributes = #[#"activityId"];
[invitationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:kActivitiesRelationship
toKeyPath:kActivitiesRelationship
withMapping:activityMapping]];
invitation = [self setupInviteProperties:STPOST];
[self setupDescriptors:invitationMapping andKeyPath:kKeyPath descriptorClass:0];
[self.objectManager.HTTPClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[self.objectManager postObject:invitation path:kKeyPath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
}
}
Descriptor:
-(void)setupDescriptors:(RKEntityMapping *)invitationMapping andKeyPath:(NSString *)keyPath descriptorClass:(BOOL)isTemplate
{
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[invitationMapping inverseMapping] objectClass:[Invite class] rootKeyPath:nil method:RKRequestMethodAny];
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:invitationMapping
method:RKRequestMethodGET
pathPattern:keyPath
keyPath:nil
statusCodes:statusCodeSet];
self.objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[self.objectManager addRequestDescriptor:requestDescriptor];
[self.objectManager addResponseDescriptor:responseDescriptor];
}
I POST just fine. When I get a Response, I get the assigned ID's, for the created objects in the database, back from the server.
POSTING: #"inviteId" : #"0" #"activityId" : #"0"
In response, I get everything back as POSTED, however, the server issues Real IDs.
RESPONSE: #"inviteId" : #"123456" #"activityId" : #"234567"
Problem:
invite Object gets updated with the assigned ID auto-magically; however, the activity objects become duplicates in CoreData (.sqlite) with one of the duplicate will have #"0" as the activityID, while its duplicate will have the assigned Id #"234567"
Later when I GET, only the assigned Id object (#"234567") get updated. Objects with#"0"` Id's are never touched and remain in Core Data.
Question: How can I avoid the duplicate record and why is this not happening to the invite Object
* UPDATE *
Returned JSON:
{
"inviteId": 226,
"meetingType": 2,
"activities": [{
"locationAddress": "4th Floor",
"startTime": "2014-05-07T01:15:23Z",
"activityId": 263,
"latitude": "0",
"longitude": "0",
"meetupId": 226,
"locationName": "West Conference Room",
"oldId": -7360
}, {
"locationAddress": "123 Main St. ",
"startTime": "2014-05-06T20:15:45Z",
"activityId": 264,
"latitude": "0",
"longitude": "0",
"meetupId": 226,
"locationName": "Starwood Building",
"oldId": -9325
}],
"comment": "Comments",
"status": 0,
"senderId": "Userid",
"startDate": "2014-05-07T13:15:00Z"
}
activities is the name of the relationship between Invite & Activity entities
This is the expected (if perhaps not ideal) outcome - RestKit is only able to map the response onto the sent object (not its relationships).
A good option could be to use fetch request blocks to find and purge the orphan activity objects at a later date (it wouldn't run after the POST, so you would need to manually find and delete if you needed the dead objects removed immediately).
Related
I am having the following problem: I use RestKit to get objects form a REST Api. The object mapping works, which I can see from the RK Debugger Output. However, when I perform a fetch request afterwards, the result is empty. I am talking about NSManagedObjects. I have the following setup.
1: Restkit and Coredata stack initialization:
NSError *error;
NSURL *baseURL = [NSURL URLWithString:#"https://mfihost.us/gocoffee/api/V1/"];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
[objectManager.HTTPClient setDefaultHeader:#"Token" value:[NSString stringWithFormat:#"%#",[FBSDKAccessToken currentAccessToken].tokenString]];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
//[RKObjectManager setSharedManager:objectManager];
[FetchRequests registerFetchRequests:objectManager];
[Descriptors registerDescriptors:objectManager];
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add inmemory store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
2: call to get objects from server and performing a fetch request afterwards:
[[RKObjectManager sharedManager]
postObject:nil path:#"/gocoffee/api/V1/login/IOS"
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Objects have been saved in core data.");
NSManagedObjectContext *managedObjCtx = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
// Shout* sh=[managedObjCtx insertNewObjectForEntityForName:#"Shout"];
// sh.inviterUserId=#"22223";
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Shout" inManagedObjectContext:managedObjCtx];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *result = [managedObjCtx executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
} else {
NSLog(#"%#", result);
}
completionBlock();
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
}];
The fetch result is empty, although the server returns objects and these objects are properly mapped by using RKEntityMappings and the corresponding response descriptors. Confusingly, if I uncomment the two lines //Shout * .... (i.e. manually insert a managed oject into the context), then this object is fetched by the fetch request. Consequently, the fetch request should be working fine.
I am searching for ages now what the problem might be. Could it be that I am calling on the wrong context or something ? By the way: core-data multi-threading debugging is enabled and does not show any error, i.e. no "AllThatIsLeftToUsIsHonor" error.
The corresponding route from the above example is:
[objectManager.router.routeSet
addRoute:[RKRoute routeWithName:#"loginAndOrSignup"
pathPattern:#"login/IOS"
method:RKRequestMethodPOST]
];
The descriptors look like (example):
[objectManager addResponseDescriptor:
[RKResponseDescriptor responseDescriptorWithMapping:[shoutMapping inverseMapping]
method:RKRequestMethodPOST
pathPattern: #"login/IOS"
keyPath:#"response.incomingshoutapplications"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
]
];
Shout mapping is as follows:
RKEntityMapping *shoutMapping = [RKEntityMapping mappingForEntityForName:#"Shout" inManagedObjectStore:managedObjectStore];
shoutMapping.identificationAttributes = #[ #"id" ];
[shoutMapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"inviterUserId" : #"inviterUserId",
#"eventType" : #"eventType",
#"eventTime" : #"eventTime",
#"locationLat" : #"locationLat",
#"locationLng" : #"locationLng",
#"radius" : #"radius",
#"targetGender" : #"targetGender",
#"state" : #"state",
#"buddyOnly" : #"buddyOnly",
#"timeCreated" : #"timeCreated"
}
];
The "manager" ist the one from above, the managedObjectStore is manager.managedObjectStore
All the mappings and descriptors are setup in another method that is calles by [Descriptors registerDescriptors:objectManager]; (see first block of code)
The problem is probably that you're using inverseMapping. That is used mainly for request mapping, because it's a mapping that creates instances of NSMutableDictionary, which isn't what you want.
So, what I expect is happening is that the response is mapped successfully, but to plain objects, not managed objects, and then you're throwing the result away.
Just change to
... responseDescriptorWithMapping:shoutMapping ...
I am retrieving some data from an API resource and I want to store the result inside my City entity using Magical Record and when the process finish, reload a tableView in my ViewController with the results.
All is fine but when I start the app for the first time,dowload process is started and the data is saved in core data.
but the table view in my ViewControllers is empty.
If I launch the app after the first time
the tableView refresh correctly.
I don't know if the problem is in threads... Can anybody help me?
ViewController :
Here I start the request. When block is called, I store cities array and reload tableView
- (void)getCitiesFromDataStore {
[[APIManager sharedManager] getCitiesWithCompletion:^(NSArray *cities) {
_dataSourceArray = cities;
[self.citiesTableView reloadData];
} failure:^(NSError *error) {
NSLog(#"%#",error.localizedDescription);
}];
}
APIMAnager
- (void)getCitiesWithCompletion:(void (^)(NSArray *))succesBlock
failure:(void (^)(NSError *))errorBlock
{
NSArray *cachedCities = [City findAllCities];
if ([cachedCities count] == 0) {
[self GET:#"cities" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *results = responseObject[#"cities"];
[City MR_importFromArray:results];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
NSArray *cities = [City findAllCities];
succesBlock(cities);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
return;
}
// Si ya hay ciudades almacenadas en CoreData, devuelvo el
// succesblock con las ciudades de CoreData
succesBlock(cachedCities);
}
I have a Category also to manage actions with the City entity
City+DBOperations
+ (NSArray *)findAllCities
{
NSArray *cities = [City MR_findAll];
return cities;
}
I know you said you resolved it, but for others who might be coming here another thing you could try is wrapping the import in a saveWithBlock:completion: and do your find in the completion block.
Also make sure you know which context each method is using. It is often helpful to be explicit about that.
Therefore you could change it to (this is untested, but should give you the concept):
[self GET:#"cities" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *results = responseObject[#"cities"];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
[City MR_importFromArray:results inContext:localContext];
} completion:^(BOOL contextDidSave, NSError *error) {
NSArray *cities = [User MR_findAllInContext:[NSManagedObjectContext MR_defaultContext]];
succesBlock(cities);
}];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
I'm trying to post new managed object to the server by using rest kit, but I don't know what I'm doing wrong. I'm getting exception like the following:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'RKRequestDescriptor objects must be initialized with a mapping whose target class is NSMutableDictionary, got 'Users' (see [RKObjectMapping requestMapping])'
I was looking for solution in stack overflow posts like this one
This is my entity mapping method from MappingProvider class:
+(RKMapping *)usersMapping
{
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Users" inManagedObjectStore:[[DateModel sharedDataModel]objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"user_id",
#"address1": #"address1",
#"address2": #"address2",
#"created_at":#"created_at",
#"updated_at": #"updated_at",
#"email": #"email",
#"name":#"name",
#"password_digest": #"password_digest",
#"phone_no": #"phone_no",
#"postcode":#"postcode",
#"remember_token":#"remember_token",
#"user_type": #"user_type",
#"apns_token":#"apns_token"
}
];
[mapping addRelationshipMappingWithSourceKeyPath:#"admins" mapping:[MappingProvider adminsMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"carers" mapping:[MappingProvider carersMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"customers" mapping:[MappingProvider customersMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"userWearers" mapping:[MappingProvider customersMapping]];
return mapping;
This is the method called when user fill all the textfields and click the register button:
-(void)registerUser
{RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[MappingProvider usersMapping] method:RKRequestMethodPOST pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
//here Xcode return exception
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[MappingProvider usersMapping] objectClass:[Users class] rootKeyPath:nil method:RKRequestMethodPOST];
[[DateModel sharedDataModel]addResponseDescriptor:userResponseDescriptor];
[[DateModel sharedDataModel]addRequestDescriptor:requestDescriptor];
RKManagedObjectStore *objectStore = [[DateModel sharedDataModel]objectStore];
Users *user = [NSEntityDescription insertNewObjectForEntityForName:#"Users" inManagedObjectContext:objectStore.mainQueueManagedObjectContext];
user.email = _email;
user.password_digest =_password;
user.name = _name;
user.address1 = _address;
user.postcode = [NSNumber numberWithInteger:[_postcode integerValue]];
user.phone_no = [NSNumber numberWithInteger:[_mobileNumber integerValue]];
[[RKObjectManager sharedManager] postObject:user path:nil parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success saving user");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure saving user: %#", error.localizedDescription);
}];
}
Date-model setup method:
- (void)setup {
self.objectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"Data.sqlite"];
NSLog(#"Setting up store at %#", path);
[self.objectStore addSQLitePersistentStoreAtPath:path
fromSeedDatabaseAtPath:nil
withConfiguration:nil
options:[self optionsForSqliteStore]
error:nil];
[self.objectStore createManagedObjectContexts];
//Configure a managed object cache to ensure we do not create duplicate objects
self.objectStore.managedObjectCache =[[RKInMemoryManagedObjectCache alloc]initWithManagedObjectContext:self.objectStore.persistentStoreManagedObjectContext];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:self.objectStore];
}
The purpose of the request descriptor is to convert your custom object into an NSMutableDictionary so that it can be serialised and sent. The mapping you're currently using is for converting into a Users object, so you need to use a different mapping.
RestKit has a convenience method that you can use:
... requestDescriptorWithMapping:[[MappingProvider usersMapping] inverseMapping] ...
I have an application that has lots of data in tables, and I'm writing a mobile app that gets a small portion of the data. The data in tables uses IDs for relations, and I'm trying to load it into a Core Data model and preserve the relationships.
But it doesn't seem like there's an easy way to tell Core Data, "For this relationship, I want a foreign key to this other table." Instead, I have this monstrosity (this is a simplified version of only one of six asynchronous RestKit queries that are coming back to fill the database):
[manager postObject:nil path:#"api/shiftservice/get" parameters:#{#"workerIds": #[#1]} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Loaded shifts: %d", [[mappingResult array] count]);
NSManagedObjectContext *context = [AppDelegate managedObjectContext];
NSSet *positions = [context fetchObjectsForEntityName:#"NWPosition"];
NSDictionary *positionDict = [NSMutableDictionary new];
for (NWPosition *position in positions) [positionDict setValue:position forKey:position.positionId.stringValue];
NSSet *workers = [context fetchObjectsForEntityName:#"NWWorker"];
NSDictionary *workerDict = [NSMutableDictionary new];
for (NWWorker *worker in workers) [workerDict setValue:worker forKey:worker.workerId.stringValue];
NSSet *shifts = [context fetchObjectsForEntityName:#"NWShift"];
for (NWShift *shift in shifts)
{
NWPosition *position = [positionDict valueForKey:shift.positionId.stringValue];
position.shifts = [context fetchObjectsForEntityName:#"NWShift" withPredicateFormat:#"positionId = %d", position.positionId];
shift.position = position;
NSSet *tradesAsGetShift = [context fetchObjectsForEntityName:#"NWTrade" withPredicateFormat:#"getShiftId = %#", shift.shiftId];
for (NWTrade *trade in tradesAsGetShift) trade.getShift = shift;
shift.tradesAsGetShift = tradesAsGetShift;
NSSet *tradesAsGiveShift = [context fetchObjectsForEntityName:#"NWTrade" withPredicateFormat:#"giveShiftId = %#", shift.shiftId];
for (NWTrade *trade in tradesAsGiveShift) trade.giveShift = shift;
shift.tradesAsGiveShift = tradesAsGiveShift;
NWWorker *worker = [workerDict valueForKey:shift.workerId.stringValue];
worker.shifts = [context fetchObjectsForEntityName:#"NWShift" withPredicateFormat:#"workerId = %d", worker.workerId];
shift.worker = worker;
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed to load shifts with error: %#", [error localizedDescription]);
}];
I'm using a modified version of Matt Gallagher's One-Line Fetch at http://www.cocoawithlove.com/2008/03/core-data-one-line-fetch.html for the fetchObjectsForEntityName.
Anyway, this seems pretty horrible to me, like I'm missing something obvious. Is there some way to just tell Core Data about database-style foreign keys? Is there an easy way to populate them, if there isn't? Because doing this many fetches for every single entity sure doesn't seem like the right way to do it. And if RestKit helps out here, so much the better.
Nope, but there should be no need to set the inverse relationships.
Thanks to Wain and his insightful comment, I managed to get RestKit to do this for me in a reasonably-elegant fashion. Here is how I set up the foreign key relationships. I set up both ends of each relationship so that either set of data could come in first and the relationship would still get populated properly.
First, I set up the manager, telling it to use JSON serialization:
RKManagedObjectStore *store = [AppDelegate managedObjectStore];
RKObjectManager *manager = [[RKObjectManager alloc] initWithHTTPClient:oauthClient];
[RKObjectManager setSharedManager:manager];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:RKMIMETypeJSON];
[manager.HTTPClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[manager registerRequestOperationClass:[AFJSONRequestOperation class]];
[manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[manager setRequestSerializationMIMEType:RKMIMETypeJSON];
manager.managedObjectStore = store;
Next, I set up connections and response descriptors:
NSManagedObjectContext *context = [AppDelegate managedObjectContext];
NSDictionary *orgRels = [[NSEntityDescription entityForName:#"NWOrg" inManagedObjectContext:context] relationshipsByName];
NSDictionary *positionRels = [[NSEntityDescription entityForName:#"NWPosition" inManagedObjectContext:context] relationshipsByName];
// ...
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
NSArray *orgConnections =
#[[self connectionForRelation:#"positions" localKey:#"orgId" foreignKey:#"orgId" withRels:orgRels],
[self connectionForRelation:#"workers" localKey:#"orgId" foreignKey:#"orgId" withRels:orgRels]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:
[self entityMappingForName:#"NWOrg" fromDictionary:#{#"orgId": #"orgId", #"orgName": #"orgName"} withKey:#"orgId" andConnections:orgConnections]
method:RKRequestMethodAny pathPattern:#"api/orgservice/get" keyPath:nil statusCodes:statusCodes]];
NSArray *positionConnections =
#[[self connectionForRelation:#"org" localKey:#"orgId" foreignKey:#"orgId" withRels:positionRels],
[self connectionForRelation:#"shifts" localKey:#"positionId" foreignKey:#"positionId" withRels:positionRels]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:
[self entityMappingForName:#"NWPosition" fromDictionary:#{#"positionId": #"positionId", #"orgId": #"orgId", #"name": #"positionName"} withKey:#"positionId" andConnections:positionConnections]
method:RKRequestMethodAny pathPattern:#"api/positionservice/get" keyPath:nil statusCodes:statusCodes]];
// ...
Then, I was able to do the simple request, and it worked great:
[manager postObject:nil path:#"api/shiftservice/get" parameters:#{#"workerIds": #[#1]}
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Loaded shifts: %d", [[mappingResult array] count]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed to load shifts with error: %#", [error localizedDescription]);
}];
Note: This used a few helper methods, which I will reproduce here:
- (RKConnectionDescription *)connectionForRelation:(NSString *)relation
localKey:(NSString *)localKey foreignKey:(NSString *)foreignKey
withRels:(NSDictionary *)rels
{
return [[RKConnectionDescription alloc] initWithRelationship:rels[relation]
attributes:#{localKey: foreignKey}];
}
- (RKEntityMapping *)entityMappingForName:(NSString *)name
fromDictionary:(NSDictionary *)dict withKey:(NSString *)key
{
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:name
inManagedObjectStore:[AppDelegate managedObjectStore]];
[mapping addAttributeMappingsFromDictionary:dict];
mapping.assignsDefaultValueForMissingAttributes = YES;
mapping.assignsNilForMissingRelationships = YES;
mapping.identificationAttributes = #[key];
return mapping;
}
- (RKEntityMapping *)entityMappingForName:(NSString *)name
fromDictionary:(NSDictionary *)dict withKey:(NSString *)key
andConnections:(NSArray *)connections
{
RKEntityMapping *mapping = [self entityMappingForName:name
fromDictionary:dict withKey:key];
for (RKConnectionDescription *connection in connections)
{
[mapping addConnection:connection];
}
return mapping;
}
i'm using restkit to map a json with core data
i need to call a routine every time the user launch the app.
if the server has updated data to send, i need to download them, truncate my table and insert the data in the table, if the server sends me nothing i don't have to do nothing.
obviously i want to truncate my current data only when i'm sure that new data has been downloaded, not before
how can I achieve this?
this is my code:
NSURL *endpoint = [NSURL URLWithString:kBaseURL];
RKObjectManager* objectManager = [RKObjectManager managerWithBaseURL:endpoint];
[objectManager.HTTPClient setAuthorizationHeaderWithToken:#"username:my-token-12345"];
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];
[RKObjectManager setSharedManager:objectManager];
RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:#"Medic" inManagedObjectStore:objectManager.managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:#{
#"identifier": #"identifier",
#"name": #"name",
#"surname": #"surname",
#"personalAddress": #"personalAddress",
#"hospital": #"hospital",
#"hospitalAddress": #"hospitalAddress",
#"oldDigitalAgreement": #"oldDigitalAgreement",
#"oldPaperAgreement": #"oldPaperAgreement"}];
entityMapping.identificationAttributes = #[#"identifier"];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:entityMapping
method:RKRequestMethodAny
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
[[RKObjectManager sharedManager] getObjectsAtPath:kregistryURL
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// done
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:NSLocalizedString(#"attention", nil) message:NSLocalizedString(#"communication.genericerror.title", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[alertView show];
}
];
solution after Wain answer
[objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
RKPathMatcher* pathMatcher = [RKPathMatcher pathMatcherWithPattern:kregistryURL];
NSDictionary *dic = nil;
if ([pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:YES parsedArguments:&dic]) {
NSFetchRequest *fetchRequest = [NSFetchRequest new];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Medic"
inManagedObjectContext: [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext];
fetchRequest.entity = entity;
return fetchRequest;
} else
return nil;
}];
Your problem is
if the server sends me nothing, i don't have to do _any_thing
and I'm guessing the server still sends a 200 response rather than a special status code (it should be 304 ‘Not Modified’). Because if you didn't have that requirement you could use a deletion fetch request block (which is the correct solution).
As it stands, you will likely have to do the deletion yourself by fetching the existing items from the data store and iterating over them, checking if the item is in the mapping results provided to you in the success block and, if not, deleting it from the context...