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;
}
Related
I setup a background thread with the Parent/Child model. Essentially the context save is failing.
Here is my setup. In the AppDelegate i've setup the _managedObjectContext with the NSMainQueueConcurrencyType:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];//[[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
In my data loading class I setup the parent/child mocs here to perform the work on the background thread:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[moc setParentContext:mainMOC];
[moc setUndoManager:nil];
When the json data has completed I attempt to peform a save operation with the following macro:
#define SAVE_MOC { NSError *error; \
if (![moc save:&error]) { NSLog(#"Sub MOC Error"); } \
[mainMOC performBlock:^{ NSError *e = nil; if (![mainMOC save:&e]) {
NSLog(#"Main MOC Error %#",error.localizedDescription);}}];}
Also when i've completed the data load I jump back on the main thread like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"<---- complete CS sub moc! ---->");
//this fires ok
});
So, from my SAVE_MOC macro i just get a simple error:
Main MOC Error (null)
Let me know if I can provide more info. I'm very new to multi-threading and trying to get a better handle on this approach.
Thanks,
Josh
In my data loading class I setup the parent/child mocs here to perform
the work on the background thread:
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
You should not do that. Do this instead.
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
Make sure you access the MOC in a performBlock. For example,
[moc performBlock:^{
// Anything at all involving this MOC or any of its objects
}];
When the json data has completed I attempt to peform a save operation
with the following macro:
Consider saving with something like this. Your completion block will be called when the save has finished.
- (void)saveMOC:(NSManagedObjectContext*)moc
completion:(void(^)(NSError *error))completion {
[moc performBlock:^{
NSError *error = nil;
if ([moc save:&error]) {
if (moc.parentContext) {
return [self saveMOC:moc.parentContext completion:completion];
}
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error);
});
}
}];
}
[self saveMOC:moc completion:^(NSError *error) {
// Completion handler is called from main-thread, after save has finished
if (error) {
// Handle error
} else {
}
}];
EDIT
This code will crash if moc.parentContext is main concurrency type. –
Mundi
There is no inherent reason that the code I posted should cause a crash with a parent MOC of NSMainQueueConcurrencyType. It has supported being a parent context ever since parent/child was added to Core Data.
Maybe I was missing a typo, so I copy/paste saveMOC:completion: straight from this answer, and wrote the following test helper.
- (void)testWithChildConcurrencyType:(NSManagedObjectContextConcurrencyType)childConcurrencyType
parentConcurrencyType:(NSManagedObjectContextConcurrencyType)parentConcurrencyType {
NSAttributeDescription *attr = [[NSAttributeDescription alloc] init];
attr.name = #"attribute";
attr.attributeType = NSStringAttributeType;
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
entity.name = #"Entity";
entity.properties = #[attr];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = #[entity];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
NSManagedObjectContext *parent = [[NSManagedObjectContext alloc] initWithConcurrencyType:parentConcurrencyType];
parent.persistentStoreCoordinator = psc;
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:childConcurrencyType];
child.parentContext = parent;
NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:child];
[obj setValue:#"value" forKey:#"attribute"];
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:#"save from %# to %# finished", concurrencyTypeString(childConcurrencyType), concurrencyTypeString(parentConcurrencyType)]];
[self saveMOC:child completion:^(NSError *error) {
// Verify data saved all the way to the PSC
NSManagedObjectContext *localMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
localMoc.persistentStoreCoordinator = psc;
NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
XCTAssertEqualObjects(#"value", [[[localMoc executeFetchRequest:fr error:NULL] firstObject] valueForKey:#"attribute"]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:nil];
}
And then, I wrote a test for each possible parent/child relationship.
- (void)testThatDoingRecursiveSaveFromPrivateToPrivateWorks {
[self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromPrivateToMainWorks {
[self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
parentConcurrencyType:NSMainQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToPrivateWorks {
[self testWithChildConcurrencyType:NSMainQueueConcurrencyType
parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToMainWorks {
[self testWithChildConcurrencyType:NSMainQueueConcurrencyType
parentConcurrencyType:NSMainQueueConcurrencyType];
}
So, what am I missing?
As I write this, I am reminded of a 360iDev presentation where the presenter said that you can't call performBlock on a NSMainQueueConcurrencyType context. At the time, I thought he just misspoke, meaning confinement, but maybe there is some confusion in the community about this.
You can't call performBlock on a NSConfinementConcurrencyType MOC, but performBlock is fully supported for NSMainQueueConcurrencyType.
I'm using RestKit and RRNCollapsableTable. The problem is, when I load view for the first time, RestKit is downloading data from JSON. That delay causes menu to not load. What I'm trying to do is to make CollapsableTable wait for data.
[self requestData:^(BOOL success) {
if (success) {
_menu = [self buildMenu];
[self model];
}
}];
- (void)requestData:(void (^)(BOOL success))completionBlock {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Messages"];
NSSortDescriptor *byId = [NSSortDescriptor sortDescriptorWithKey:#"customNewsId" ascending:YES];
fetchRequest.sortDescriptors = #[byId];
NSError *error = nil;
// Setup fetched results
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext
sectionNameKeyPath:#"customNewsId"
cacheName:nil];
//[self.fetchedResultsController setDelegate:self];
BOOL fetchSuccessful = [self.fetchedResultsController performFetch:&error];
if (!fetchSuccessful) {
abort();
}
[[RKObjectManager sharedManager] getObjectsAtPath:#"api/json/get/bZmroLaBCG" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogInfo(#"Load complete: Table should refresh...");
//[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"LastUpdatedAt"];
//[[NSUserDefaults standardUserDefaults] synchronize];
NSError *error = nil;
if ([[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext hasChanges] && ![[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext saveToPersistentStore:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
completionBlock(YES);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
completionBlock(NO);
}];
completionBlock(YES);
}
Then BuildMenu is just iterating over fetched objects and put them in section.
-(NSArray *)buildMenu {
__block NSMutableArray *collector = [NSMutableArray new];
NSInteger sections = [self.fetchedResultsController.sections count];
for (NSInteger i = 0; i < sections; i++) {
Messages *message = [_fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:i]];
MenuSection *section = [MenuSection new];
section.items = #[message.message,message.pushNotificationMessage];
section.title = message.title;
NSLog(#"section.title - %#",section.title);
[collector addObject:section];
}
return [collector copy];
}
Method model is responsible for DataSource for CollapsableTable.
-(NSArray *)model {
return _menu;
}
Thanks in advance for any help.
Greetings.
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...
First question here and I've tried a bunch of stuff and can't figure it out.
Core Data with 2 entities with to-many relationship both ways
A<<---->>B
A entity has name as an attribute, and a relationship Bs
First Controller lists all A entities and i have a second controller to add A entities and I want to have it save a default B in its relationship.
In the prepare for segue I have this code:
if ([[segue identifier] isEqualToString:#"addAEntitySegue"]) {
AddAEntityViewController *addAEntityViewController = [segue destinationViewController];
addAEntityViewController.delegate = self;
addAEntityViewController.managedObjectContext = self.managedObjectContext;
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
addAEntityViewController.A = [NSEntityDescription insertNewObjectForEntityForName:#"A" inManagedObjectContext:addingContext];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:addAEntityViewController
action:#selector(save:)];
addAEntityViewController.navigationItem.rightBarButtonItem = saveButton;
}
In addAEntityViewController i have this to save
-(IBAction)save:(id)sender
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"B" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#",[defaults objectForKey:#"BDefault"]];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
//Set the batch size to a suitable number
[fetchRequest setFetchBatchSize:20];
NSError *error;
self.A.name = textFieldVal;
[self.A addBObject:[[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] objectAtIndex:0]];
NSLog(#"A = %#", self.A.Bs);
[self.delegate addAEntityViewController:self didFinishWithSave:YES];
}
In the addAEntityViewController everything saves correctly even the NSLog(#"A = %#", self.A.Bs); statement shows the B. But when the delegate saves in the First Controller (AEntityViewController) it only saves the A.name but not the relationship A.Bs, I can't figure out what's wrong.
Here's the delegate method:
-(void) addAEntityViewController:self didFinishWithSave:YES{
if (save) {
NSLog(#"saveworkouts");
NSError *error;
if (![addingManagedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
// release the adding managed object context
self.addingManagedObjectContext = nil;
}
Like I said it saves the A entity but not the relationship to B even though the relationship to B saved correctly in the addAEntityViewController (the second View). An NSLOg of A.Bs is null.
First I believe that this line:
addAEntityViewController = self.managedObjectContext;
should be:
addAEntityViewController.managedObjectContext = self.managedObjectContext;
but that would also be wrong.
it should be getting the addingContext you created afterwards:
addAEntityViewController.managedObjectContext = addingContext;
I'm a bit surprised that your app didn't crash, as you are mixing managed objects from 2 different contexts.
In an iOS6 app I fetch NSManagedObjects from DB with CoreData and display them in a tableViewCell. My problem is, that all objects that correspond to cells outside of the initial scroll position are in fault state and do not come back to life. I can't see my mistake.
fetchRequest.returnsObjectsAsFaults = NO; helps, but I want a clean solution.
Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ContactsCell";
ContactsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Contact *contact = [self.contacts objectAtIndex:indexPath.row];
//here some contacts are faulted. contact.name is null if I fetch it
[cell setContactData:contact];
return cell;
}
here is how I fetch (with Restkit 0.10.3):
NSFetchRequest *fetchRequest = [Contact fetchRequest];
fetchRequest.sortDescriptors = [self sortDescriptors];
fetchRequest.returnsObjectsAsFaults = NO;
return [Contact objectsWithFetchRequest:fetchRequest];
Ok I never really used your approach so I won't say that it is wrong, but I'll say that it is strange. I guess that Contact is a subclass of NSManagedObject, and I can believe that it knows of the fetch request which which he was originally fetched, and that he knows of the context from which he was fetched, but only after he was already fetched. I really don't see how could he know of those things if he never before was fetched from the persistent store. So I recommend U use classic executeFetch or fetchedResultsController to populate your tableView.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:persistentStoreCoordinator];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:context];
fetchRequest.entity = entity;
fetchRequest.sortDescriptors = [self sortDescriptors];
NSArray *array = [context executeFetchRequest:fetchRequest error:&error];
return array;
Try it, hope it helps