I have an iOS app that uses restkit to handle json responses to map things to master data. Every time I execute a query using the RKObjectManager get / post / put / delete methods, it works fine and I never run into any problems.
The application I am developing also supports socket updates, for which I use SocketIO for processing. SocketIO also works flawlessly, and every event that the server sends, I receive without fail if the application does not work at this time.
The problem occurs when I try to extract json data from a socket event and match it with the main data. If the socket event arrives at the same time, the response is returned from the request I made through RKObjectManager, and both of them give me the same object for the first time, they often both make 2 copies of the same ManagedObject in coredata, and I get the following warning in console:
Managed Object The cache returned 2 objects for the identifier configured for the object [modelObjectName], expected 1.
Here is my method containing the code to create RKMapperOperation:
+(void)createOrUpdateObjectWithJSONDictionary:(NSDictionary*)jsonDictionary
{
RKManagedObjectStore* managedObjectStore = [CMRAManager sharedInstance].objectManager.managedObjectStore;
NSManagedObjectContext* context = managedObjectStore.mainQueueManagedObjectContext;
[context performBlockAndWait:^{
RKEntityMapping* modelEntityMapping = [self entityMappingInManagedObjectStore:managedObjectStore];
NSDictionary* modelPropertyMappingsByDestinationKeyPath = modelEntityMapping.propertyMappingsByDestinationKeyPath;
NSString* modelMappingObjectIdSourceKey = kRUClassOrNil([modelPropertyMappingsByDestinationKeyPath objectForKey:NSStringFromSelector(@selector(object_Id))], RKPropertyMapping).sourceKeyPath;
NSString* modelObjectId = [jsonDictionary objectForKey:modelMappingObjectIdSourceKey];
CMRARemoteObject* existingObject = [self searchForObjectOfCurrentClassWithId:modelObjectId];
RKMapperOperation* mapperOperation = [[RKMapperOperation alloc]initWithRepresentation:jsonDictionary mappingsDictionary:@{ [NSNull null]: modelEntityMapping }];
[mapperOperation setTargetObject:existingObject];
RKManagedObjectMappingOperationDataSource* mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc]initWithManagedObjectContext:context cache:managedObjectStore.managedObjectCache];
[mappingOperationDataSource setOperationQueue:[NSOperationQueue new]];
[mappingOperationDataSource setParentOperation:mapperOperation];
[mappingOperationDataSource.operationQueue setMaxConcurrentOperationCount:1];
[mappingOperationDataSource.operationQueue setName:[NSString stringWithFormat:@"%@ with operation '%@'", NSStringFromSelector(_cmd), mapperOperation]];
[mapperOperation setMappingOperationDataSource:mappingOperationDataSource];
NSError* mapperOperationError = nil;
BOOL mapperOperationSuccess = [mapperOperation execute:&mapperOperationError];
NSAssert((mapperOperationError == nil) && (mapperOperationSuccess == TRUE), @"Execute mapperOperation error");
if (mapperOperationError || (mapperOperationSuccess == FALSE))
{
NSLog(@"mapperOperationError: %@",mapperOperationError);
}
NSError* contextSaveError = nil;
BOOL contextSaveSuccess = [context saveToPersistentStore:&contextSaveError];
NSAssert((contextSaveError == nil) && (contextSaveSuccess == TRUE), @"Save context error");
}];
}
In the above code, I will first try and fetch the existing managed entity, if it currently exists, to set it to the target of the mapping request. The method for finding an object (searchForObjectOfCurrentClassWithId :) is as follows:
+(instancetype)searchForObjectOfCurrentClassWithId:(NSString*)objectId
{
NSManagedObjectContext* context = [CMRAManager sharedInstance].objectManager.managedObjectStore.mainQueueManagedObjectContext;
__block CMRARemoteObject* fetchedObject = nil;
[context performBlockAndWait:^{
NSFetchRequest* fetchRequest = [self fetchRequestForCurrentClassObjectWithId:objectId];
NSError* fetchError = nil;
NSArray *entries = [context executeFetchRequest:fetchRequest error:&fetchError];
if (fetchError)
{
NSLog(@"fetchError: %@",fetchError);
return;
}
if (entries.count != 1)
{
return;
}
fetchedObject = kRUClassOrNil([entries objectAtIndex:0], CMRARemoteObject);
if (fetchedObject == nil)
{
NSAssert(FALSE, @"Should be of this class");
return;
}
}];
return fetchedObject;
}
, , , . , , Restkit. , , Restkit , , - .
, . RKEntityMapping, , . , Restkit , mapper, / JSON .
, , , , iPhone 5c iPod touch, iPod touch, , , 1 , iPhone 5c , , . , , , , , , , , , , .