Accessing property without self.propertyname cause errors - objective-c

I'm working on App with coreData and i get an error that i can't understand: In a custom init whether i define NSFetchedResultController with "self.controller = new_controller" all works correctly, if i define only "controller = new_controller" (without self), error "The NSManagedObject has been invalidated" occur after 2 loops for the situation described in this image:
Application parts interested in this error is here described:
A Table with a list of Months (Class MonthListVC, step 1 in the image). Where
sections describe Month (like April
2011) and the unique Row for any
section present data (that are sum
from transactions happened during the
month).
Selecting one of these Row a next
Table is pushed in navigation
controller (Class WeekTVC, step 2 in the image), this table presents
detail for every weeks for selected month. Here section title has format "week beginning - week end", and the unique row for the section is a sum of transactions for the week.
A NSManagedObjectContext is passed via a custom init method (check the code)
every data for these tables are managed with
NSFetchedResultController.
Here the code for selection on first Table (MonthListVC)
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:indexPath.section];
// get the first object for this section and get its date.
NSDate *datepoint = [[[sectionInfo objects]objectAtIndex:0] date];
//Create next Controller and push it into Navigation
WeeksTVC *weeksTVC = (WeeksTVC*)[[WeeksTVC alloc] initWithContext:self.context datepoint:datepoint withTitle: [sectionInfo name] ];
[self.navigationController pushViewController:weeksTVC animated:YES];
[weeksTVC release];
}
And here the code for WeeksTVC custom init method, where i define "self.controller = ..." and where i get the problem whether i write only "controller = ...".
"controller" is a synthesized property.
-(WeeksTVC *)initWithContext:(NSManagedObjectContext *)n_context datepoint:(NSDate*)n_datePoint withTitle:(NSString*) stringTitle{
self = [self init];
if(self !=nil){
self.title = [NSString stringWithFormat:#"History (%#)",stringTitle];
//Back button setup
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] init];
barButton.title = #"Back";
self.navigationItem.backBarButtonItem = barButton;
//datePoint = n_datePoint;
self.context = n_context;
//Get Begin and End week information for this datePoint
NSDate *startDay = [[CommonHelper weekInfoFromData:[CommonHelper firstDayOfMonthForDate:n_datePoint]] objectForKey:#"begin"];
NSDate *endDay = [[CommonHelper weekInfoFromData:[CommonHelper lastDayOfMonthForDate:n_datePoint]] objectForKey:#"end"];
//Manage Request
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = [NSEntityDescription entityForName:#"Transactions" inManagedObjectContext:self.context];
request.predicate = [NSPredicate predicateWithFormat:#"(date >= %#) && (date <= %#)",startDay,endDay];
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
NSArray *sortDescs = [NSArray arrayWithObjects:sort,nil];
request.sortDescriptors = sortDescs;
"HERE THE PROBLEM WITH SELF***********************************************"
//Manage NSResultFetchedController
self.controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.context sectionNameKeyPath:#"byWeek" cacheName:nil];
self.controller.delegate = self;
NSError *error = nil;
//Fetch
[self.controller performFetch:&error];
"END OF PROBLEM***********************************************************"
[request release];
[barButton release];
}
return self;
}
I report also dealloc method for WeeksTVC, i found that if i don't release context and controller in this method... no error occur also if i write in init method "controller = " and not "self.controller = "
- (void)dealloc {
//[self.datePoint release];
[self.controller release];
[self.context release];
[super dealloc];
}
Why self.controller and controller are so different in this case ???
In init method i ever write ivar without self -.-'

In custo init method for WeeksTVC i wrote
self = [self init];
and not
self = [super init];
This cause the problem... but i'm curious to understand what happen exactly...

Related

Updating CollectionView with NSFetchedResultsController

I have a UICollectionView that needs to be updated when the model has changed.
It is subscribed to the notification center in order to do so:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(modeloActualizado:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.contexto];
}
The UICollectionViewController connects to the model through a NSFetchedResultsController that is meant to return a list of the entities "Plaza" whose "ocupada" value is 1 (it is a boolean).
-(NSFetchedResultsController*) frController{
if(_frController == nil){
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entidad = [NSEntityDescription entityForName:#"Plaza" inManagedObjectContext:self.contexto];
request.entity = entidad;
request.fetchBatchSize = 10;
// AƱadir un predicado para filtrar por plazas ocupadas
request.predicate = [NSPredicate predicateWithFormat:#"ocupada == 1"];//%#",[NSNumber numberWithBool:YES]];
// Ordenar por numero de plaza
NSSortDescriptor *ordenPorNumero = [[NSSortDescriptor alloc] initWithKey:#"numero" ascending:YES];
NSArray *descriptores = [[NSArray alloc] initWithObjects:ordenPorNumero, nil];
[request setSortDescriptors:descriptores];
// Crear el FetchedResultsController
//[NSFetchedResultsController deleteCacheWithName:#"Coleccion"];
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.contexto sectionNameKeyPath:nil cacheName:#"Coleccion"];
_frController.delegate = self;
NSError *error = nil;
if(![self.frController performFetch:&error]){
NSLog(#"Ha ocurrido un error: %# %#",error,[error userInfo]);
abort();
}
}
return _frController;
}
The method called when the model changed does reload the CollectionView data:
-(void)modeloActualizado:(NSNotification *) notificacion{
[self.collectionView reloadData];
}
The problem is, if I change any of the values of an entity, those changes are shown in the CollectionView, but if I change the entity boolean value "ocupada" to NO, it will still appear into the CollectionView until the App is closed and opened again.
Am I doing something wrong? I don't know why the NSFetchedResultsController is returning the same amount of objects even though one of them no longer matches the predicate condition.
Any ideas?
Thanks in advance.

store result from wcf service in core data

I am using a WCF service in my app.When the app is run for the first time on the iPad,I want it to call a WCF service and display the result in a UITableView.Alongwith displaying the data in UITableView,i want to store the data in Core Data so when the user is "offline"(not connected to wifi)the data will be displayed from the Core Data.The AppDelegate.m looks like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults objectForKey:#"firstRun"])
{
self.firstRun = TRUE;
[defaults setObject:[NSDate date] forKey:#"firstRun"];
}
else
{
self.firstRun = FALSE;//flag does exist so this ISNT the first run
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
The code in UITableView looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[my_table setDataSource:self];
[my_table setDelegate:self];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.firstRun){
NSLog(#"IS FIRST RUN");
EDViPadDocSyncService *service = [[EDViPadDocSyncService alloc]init];
[service getAllCategories:self action:#selector(handleGetAllCategories:)];
}
else
{
NSLog(#"NOT FIRST RUN");
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Categories" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *errormsg;
self.allCats = [managedObjectContext executeFetchRequest:fetchRequest error:&errormsg];
NSLog(#"allCATS=%#",self.allCats);
self.title = #"Categories";
}
}
-(void)handleGetAllCategories:(id)value
{
if([value isKindOfClass:[NSError class]])
{
NSLog(#"This is an error %#",value);
return;
}
if([value isKindOfClass:[SoapFault class]])
{
NSLog(#"this is a soap fault %#",value);
return;
}
NSMutableArray *result = (NSMutableArray*)value;
NSMutableArray *categoryList = [[NSMutableArray alloc] init];
NSMutableArray *docCount = [[NSMutableArray alloc]init];
NSMutableArray *catIdList = [[NSMutableArray alloc]init];
self.myData = [[NSMutableArray array] init];
self.myDocCount = [[NSMutableArray array]init];
self.catId = [[NSMutableArray array]init];
for (int i = 0; i < [result count]; i++)
{
EDVCategory *catObj = [[EDVCategory alloc]init];
catObj = [result objectAtIndex:i];
[categoryList addObject:[catObj categoryName]];
[docCount addObject:[NSNumber numberWithInt:[catObj docCount]]];
[catIdList addObject:[NSNumber numberWithInt:[catObj categoryId]]];
}
self.myData = categoryList;
self.myDocCount = docCount;
self.catId = catIdList;
[my_table reloadData];
/*store data in Core Data - START*/
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newCategory;
for(int j=0;j<[result count];j++)
{
newCategory = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:context];
/*HOW TO STORE DATA FOR THE "CATEGORIES" OBJECT IN CORE DATA*/
}
/*store data in Core Data - END*/
}
I am not able to figure out how to store the data received from the wcf service to the core data object directly.I know how to store it from a text box on the screen to a core data object.eg.:-
coreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newContact;
newCat = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:context];
[newCat setValue:name.text forKey:#"name"];
name.text = #"";
[context save:&error];
But this doesn't help in my case.Any help is appreciated.
You are mixing networking and UI code. It is a recipe for unmaintainable code.
Your UI should be looking at Core Data and only Core Data to display its data.
Separately, and asynchronously you should be requesting data from WCF and pushing it into Core Data.
Your UI does not need to care about first run vs. subsequent run. It just looks at Core Data via a NSFetchedResultsController.
Your network code is the only part that cares about new vs. update.
Update 1
how can I achieve this? When the app is running and connected to WiFi,it has to get the latest data from the WCF service.
NSURLConnection can do async requests built-in. I generally recommend writing your networking code as NSOperation subclasses and then put them into a queue.
It appears that WCF can return XML and takes standard HTTP requests. Therefore you can write NSOperation subclasses that build your request, send it to the server and wait for a reply. When the reply comes you parse the XML and insert it into Core Data. When you save the Core Data NSManagedObjectContext your NSFetchedResultsController instances will automatically fire and allow you to update your UI.
I have several code samples that perform these feats although they are written for JSON responses as opposed to XML responses. It would not be difficult to take those examples and alter them to your needs.
You can start with this stackoverflow question and its response.
To store the data into the attributes of your NSManagedObject, simply set the values using KVC:
EDVCategory *catObject = [result objectAtIndex:j];
[newCategory setValue:[catObject categoryName] forKey#"categoryName"];
[newCategory setValue:[catObject docCount] forKey#"docCount"];
[newCategory setValue:[catObject categoryID] forKey#"categoryID"];
// after the loop
[context save:&nil];

Pushing UITableViewControllers and filtering/reloading the data

I think this is probably a simple problem that I am not understanding but I have spent a lot of time on it and it is holding up my work.
I have a UITableViewController that displays 4 rows of "topics". When you touch one, it creates an instance of my UITableViewController class, set's the data source array to the array of "subtopics" and pushes the controller onto the nav stack. When I go to my "All" topics page with say 10 rows, I have a segmented control for filtering the results. I am able to successfully filter the array but when I assign it to my self.topicsDataSource and call [self.tableView reloadData], nothing happens. The delegate methods numberOfSections and numberOfRowsInSection, but cellForRowAtIndexPath is not. When I press the navigation back button it goes back to and the previous table is now the filtered results I wanted in the top table.
So here's what I think is happening.
1. I am actually altering the data source of the previous table, and self.tableView.reloadData is being called on that table.
2. cellForRowAtIndexPath is not called because the tableview being reloaded is not visible at that time.
Here is where I am instantiating an pushing the table views. This class is called TopicsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *idtopic = [[self.topicsDataSource objectAtIndex:indexPath.row] idtopic];
NSArray *subtopics = [DataManager getSubtopicsForTopic:idtopic];
if ([subtopics count] > 0) {
TopicsViewController *topicsVC = [[TopicsViewController alloc ] init];
topicsVC.tableView.rowHeight = 106;
topicsVC.parentTopicId = idtopic;
topicsVC.topicsDataSource = [NSMutableArray arrayWithArray:subtopics];
topicsVC.diseaseStateId = self.diseaseStateId;
topicsVC.title = [[self.topicsDataSource objectAtIndex:indexPath.row] topic];
[self.navigationController pushViewController:topicsVC animated:YES];
}
else if (![[self.topicsDataSource objectAtIndex:indexPath.row] topic]) {
//all topics was selected
TopicsViewController *topicsVC = [[TopicsViewController alloc] initWithNibName:nil bundle:nil];
topicsVC.tableView.rowHeight = 106;
NSMutableArray *mArray = [[NSMutableArray alloc] initWithArray:[DataManager getTopicsAtEndOfTree]];
topicsVC.allTopics = mArray;
topicsVC.topicsDataSource = topicsVC.allTopics;
topicsVC.diseaseStateId = self.diseaseStateId;
topicsVC.parentTopicId = [NSNumber numberWithInt:100];
UISegmentedControl *segControl = [[UISegmentedControl alloc] initWithItems:[[NSArray alloc] initWithObjects:#"All", #"Speaker Direct", #"Live Meeting", nil]];
[segControl addTarget:self action:#selector(segControlChanged:) forControlEvents:UIControlEventValueChanged];
[segControl setSegmentedControlStyle:UISegmentedControlStyleBar];
[segControl setSelectedSegmentIndex:0];
topicsVC.navigationItem.titleView = segControl;
topicsVC.tableView.dataSource = topicsVC;
[self.navigationController pushViewController:topicsVC animated:YES];
}....
and here is where I am filtering the data and reloading the tableView:
- (void)segControlChanged:(UISegmentedControl *)sender
{
self.allTopics = [NSMutableArray arrayWithArray:[DataManager getTopicsAtEndOfTree]];
if (sender.selectedSegmentIndex == 1) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"type == 2"];
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[self.allTopics filteredArrayUsingPredicate:pred]];
self.topicsDataSource = mArr;
}
else if (sender.selectedSegmentIndex == 2) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"type == 1"];
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[self.allTopics filteredArrayUsingPredicate:pred]];
self.topicsDataSource = mArr;
}
else {
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[DataManager getTopicsAtEndOfTree]];
self.topicsDataSource = mArr;
}
[self.tableView reloadData];
}
Any help would be appreciated. Thanks!
- (void)segControlChanged:(UISegmentedControl *)sender
{
self.allTopics = [NSMutableArray arrayWithArray:[DataManager getTopicsAtEndOfTree]];
if (sender.selectedSegmentIndex == 1) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"type == 2"];
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[self.allTopics filteredArrayUsingPredicate:pred]];
self.topicsDataSource = mArr;
}
else if (sender.selectedSegmentIndex == 2) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"type == 1"];
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[self.allTopics filteredArrayUsingPredicate:pred]];
self.topicsDataSource = mArr;
}
else {
NSMutableArray *mArr = [[NSMutableArray alloc] initWithArray:[DataManager getTopicsAtEndOfTree]];
self.topicsDataSource = mArr;
}
[tableView reloadData]; //
}
It's very difficult to tell what your question is and what is happening - I'd suggest next time asking a question with much less random code, more explanation, and a more obvious question (also better formatting). Anyway.
As best as I can tell, self is still referring to the original view controller. I'm not sure what classes your code is coming from but if you want this to work you need to
Write a custom class for TopicsViewController
Push it/etc
Inside the custom class, define datasource, reloading, etc
Call the change datasource method inside of the custom class
Call the reloadData method on the tableView of the custom class.
The point is, make sure that all of your work happens inside of the class for your custom table view data controller.

UITableView returns null?

I have spent many hours looking into this and I still can't find a solution. Here's a chart on what the user goes through:
The user takes a picture or takes one from their camera roll
As soon as they take (or select) one, the captionView closes
As soon as the captionView closes, startUploads from a different view starts
If the user decides to open the view that startUploads is in, they can see what has been uploaded
Now that you know a little bit about the process, I have a few issues. When uploads finish I want them to fade out. I know how to do that, but for some reason when I call to, the self.theTableView returns null. I have a #property (nonatomic, retain)... in my .h and I have a #sythesize theTableView; in my .m.
In a different question, I found out that when doing the startUploads, my NSMutableArray needed to be initiated in here (so I thought that it would do the same with my UItableView). Here's what I have:
- (id)initWithNibNamed:(NSString *)nibName bundle:(NSBundle *)bundle {
self = [super initWithNibNamed:nibName bundle:bundle];
if (self) {
processList = [[NSMutableArray alloc] init];
self.theTableView = [[UITableView alloc] init];
NSLog(#"thetableview: %#", theTableView);
}
return self;
}
Just in case you want to see how startUploads is called, here's the code:
processViewController *testtest = [[processViewController alloc] initWithNibName:#"processView.xib" bundle:nil];
//testtest.processList = [[NSMutableArray alloc] initWithNig];
NSLog(#"Different:displayProcessVC BEFORE STARTING UPLOAD, processList = %#", testtest.processList);
NSLog(#"Different:displayProcessVC BEFORE STARTING UPLOAD, theTableView = %#", testtest.theTableView);
[testtest startUploads];
NSLog(#"Different:displayProcessVC AFTER STARTING UPLOAD, processList = %#", testtest.processList);
NSLog(#"Different:displayProcessVC AFTER STARTING UPLOAD, theTableView = %#", testtest.theTableView);
[testtest release];
However it still shows (null) in the console.
Please help!Coulton
EDIT 1:
Here's where it's being nil:
- (void)deleteOneRow: (NSString *)theString {
int theInt = [processList indexOfObject:theString];
[processList removeObjectAtIndex:theInt];
NSArray *deleteIndexPaths = [[NSArray alloc] initWithObjects: [NSIndexPath indexPathForRow:theInt inSection:0], nil];
NSLog(#"theTableView: %#", self.theTableView);
[self.theTableView beginUpdates];
[self.theTableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.theTableView endUpdates];
}
The designated initializer for UITableView is -initWithFrame:style: and you're leaking. Try this in -viewDidLoad instead.
self.theTableView = [[[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain] autorelease];

Core Data mergeChangesFromContextDidSaveNotification Doesn't Work

I am writing a Core Data ContextManager for a larger iOS application. A ContextManager provides an NSManagedContext that is automatically updated when other ContextManagers save their NSMangedContext to the persistent data store.
I have a unit test (TestContextManager) that creates two contexts, adds an object to one, and tests to see if the object appears in the other context. It doesn't. Why does the last test fail?
Here's the code for a ContextManager and the failing unit test. The last assert in the unit test fails. Every other assert passes. As you can see, the ContextManager relies upon getting a change notification from a different ContextManager and using mergeChangesFromContextDidSaveNotification to update itself. Notice that everything happens on the same thread for this test.
I know the NSManagedObjectContextDidSaveNotification is being sent and received correctly. I know the NSManagedObjectContextDidSaveNotification has the correct data in its userInfo dictionary.
I have also run this unit test as an application test on an actual device using an SQLite persistent store -- the same assert fails.
Thanks in advance!
ContextManager:
#import "ContextManager.h"
#implementation ContextManager
#synthesize context;
#pragma mark - Custom code
- (void)save {
NSError *error = nil;
if (self.context != nil) {
if ([self.context hasChanges] && ![self.context save:&error]) {
NSAssert1(FALSE, #"Unable to save the managed object context. UserInfo:\n%#", [error userInfo]);
}
}
return;
}
- (void)mergeChanges:(NSNotification *)notification {
if (notification.object != self.context) {
[self.context mergeChangesFromContextDidSaveNotification:notification];
}
return;
}
#pragma mark - Overridden NSObject methods
#pragma mark Creating, copying, and deallocating object
- (id)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];
self.context = [[[NSManagedObjectContext alloc] init] autorelease];
[self.context setPersistentStoreCoordinator:persistentStoreCoordinator];
}
return self;
}
- (void)dealloc {
[context release];
[super dealloc];
return;
}
#end
TestContextManager:
#import "TestContextManager.h"
#import "ContextManager.h"
#import "CoreDataManager.h"
#define TEST_MANAGED_OBJECT #"AManagedObject"
#implementation TestContextManager
- (void)testContextManager {
CoreDataManager *coreDataManager = [[CoreDataManager alloc] init];
coreDataManager.storeType = NSInMemoryStoreType;
ContextManager *contextManagerA = [coreDataManager provideContextManager];
if (!contextManagerA) STFail(#"CoreDataManager did not provide a context manager.");
NSManagedObjectContext *contextA = contextManagerA.context;
if (!contextA) STFail(#"ContextManager did not provide a managed object context.");
// setA1 has 0 objects (or whatever is initially there).
NSSet *setA1 = [contextManagerA.context registeredObjects];
[NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context];
// setA2 has 1 object.
NSSet *setA2 = [contextManagerA.context registeredObjects];
STAssertTrue([setA2 count] == [setA1 count]+1, #"Context provided by ContextManager is not accepting new objects.");
[contextManagerA save];
ContextManager *contextManagerB = [coreDataManager provideContextManager];
[NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context];
[contextManagerB save];
NSSet *setA3 = [contextManagerA.context registeredObjects];
// setA3 should have 2 objects <=== THIS TEST FAILS
STAssertTrue([setA3 count] == [setA1 count]+2, #"Context is not updating new objects.");
[coreDataManager release];
return;
}
#end
Did you actually set up the ContextManager to observe the notification for saving a managedObjectContext? You don't show that here so I just wanted to cover the simplest case.
Sorry, I should have made this a comment on Erik's post.
Thanks to littleknown for answering my question. Clearly I need to do some reading on what registeredObjects actually returns. I guess the good news here is that the actual code works -- the unit test was bad...
Here's the unit test that correctly exercises the unit under test AND passes:
#import "TestContextManager.h"
#import "ContextManager.h"
#import "CoreDataManager.h"
#define TEST_MANAGED_OBJECT #"AManagedObject"
#implementation TestContextManager
- (void)testContextManager {
CoreDataManager *coreDataManager = [[CoreDataManager alloc] init];
coreDataManager.storeType = NSInMemoryStoreType;
ContextManager *contextManagerA = [coreDataManager provideContextManager];
if (!contextManagerA) STFail(#"CoreDataManager did not provide a context manager.");
NSManagedObjectContext *contextA = contextManagerA.context;
if (!contextA) STFail(#"ContextManager did not provide a managed object context.");
NSEntityDescription *entityDescriptionA = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextA];
// make A1 request on an empty context (0 objects)
NSFetchRequest *requestA1 = [[NSFetchRequest alloc] init];
[requestA1 setEntity:entityDescriptionA];
NSError *errorA1 = nil;
NSArray *arrayA1 = [contextA executeFetchRequest:requestA1 error:&errorA1];
if (arrayA1 == nil) STFail(#"Fetch request A1 failed.");
if ([arrayA1 count] != 0) STFail(#"Context A1 is not empty at start of test.");
// add an object to context A and make request A2
[NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context];
NSFetchRequest *requestA2 = [[NSFetchRequest alloc] init];
[requestA2 setEntity:entityDescriptionA];
NSError *errorA2 = nil;
NSArray *arrayA2 = [contextA executeFetchRequest:requestA2 error:&errorA2];
if (arrayA2 == nil) STFail(#"Fetch request A2 failed.");
if ([arrayA2 count] != 1) STFail(#"Context A2 did not successfully add an object.");
// add an object to context B and make request B1
ContextManager *contextManagerB = [coreDataManager provideContextManager];
NSManagedObjectContext *contextB = contextManagerB.context;
NSEntityDescription *entityDescriptionB = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextB];
[NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context];
NSFetchRequest *requestB1 = [[NSFetchRequest alloc] init];
[requestB1 setEntity:entityDescriptionB];
NSError *errorB1 = nil;
NSArray *arrayB1 = [contextB executeFetchRequest:requestB1 error:&errorB1];
if (arrayB1 == nil) STFail(#"Fetch request B1 failed.");
if ([arrayB1 count] != 1) STFail(#"Context B1 did not successfully add an object.");
// save contextB
[contextManagerB save];
// check if contextA was updated
NSFetchRequest *requestA3 = [[NSFetchRequest alloc] init];
[requestA3 setEntity:entityDescriptionA];
NSError *errorA3 = nil;
NSArray *arrayA3 = [contextA executeFetchRequest:requestA3 error:&errorA3];
if (arrayA3 == nil) STFail(#"Fetch request A3 failed.");
if ([arrayA3 count] != 2) STFail(#"Context A did not update correctly.");
[requestA1 release];
[requestA2 release];
[requestB1 release];
[requestA3 release];
[coreDataManager release];
return;
}
#end