AFNetworking multiple uploads slowing down main thread - iphone

I have a UITabBarController where UITableViewControllerA list files and UITableViewContollerB shows the progress of the files being uploaded.
I have a Singleton class with an upload method that calls my subclass AFHTTPClient and uses NSNotificationCenter to notify my UITableViewControllerB of the upload progress. But this current way is slowing down the UI to where it is almost unusable and I'm not sure how I can improve the process. I read that AFNetworking callback functions are called on the main thread. Is the slow UI response coming from my NSNotificationCenter?
I also would like to mention I'm running this on the Simulator.
Method from my Singleton class.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:uniqueName forKey:#"unique"];
[dict setObject:[NSNumber numberWithFloat:0] forKey:#"progress"];
[self.active addObject:dict];
[[CustomHTTP client] uploadFileName:#"filename" withBytes:data toPath:serverPath progress:^(float progress) {
[dict setObject:progress forKey:#"progress"];
NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
[info setObject:[NSNumber numberWithInt:[self getIndexByUniquename:uniqueName]] forKey:#"row"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ProgressNotification" object:self userInfo:info];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
} andFailure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
UITableViewControllerB.m
- (void) receiveTestNotification:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"ProgressNotification"]) {
NSDictionary *dict = notification.userInfo;
int row = [[dict objectForKey:#"row"] intValue];
self.inProgress = [Transfer defaultTransfer].active;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
}

You are sending to many notifications. Reloading tableview cells is a somewhat expensive operation. I would restrict the posting of notifications to only when a full percent point has changed or to only one every second.
You can play around with what works best for you, but 8300 notifications is way to much for the tableview to handle.

Instead of calling reloadRowsAtIndexPaths I changed it to find the cell and update the label that way.
for (int i = 0; i < [self.items count]; i++) {
if ([self.items objectAtIndex:i] == transfer) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
TransferCell *cell = (TransferCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.percentLabel.text = transfer.completed;
break;
}
}

Related

UIProgressView indicator not updating with the correct "progress"

I am downloading images asynchronously and displaying them in a UITableView. While theimage is downloading, a UIProgressView should be displayed in the corresponding table row. After the download is complete, progress view should be replaced by the actual image.
In my table view, I am using a custom cell called ProgressTableViewCell subclassed from UITableViewCell. It has a UIProgressView IBOutlet.
I have created an NSOperation from NSURLConnection and added them to an NSOperationQueue. As the delegate's
didReceiveData
method is called, a notification is posted to my table view controller to update the corresponding table row with
reloadRowsAtIndexPaths
method of table view. My cellForRowAtIndexPath does the following for the reloaded row:
ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:#"ProgressCell"];
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSNumber* percentage= [NSNumber numberWithFloat:received/total];
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
NSLog(#"percentage %f", percentage.floatValue);
[userInfo setObject:cell forKey:#"cell"];
[userInfo setObject:percentage forKey:#"percentage"];
[self performSelectorOnMainThread:#selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];
NSLog(#"received: %#", [downloadInfo objectForKey:#"receivedBytes"]);
NSLog(#"Progress: %f", cell.progressView.progress);
return cell;
The updateProgressView method looks like
- (void)updateProgressView :(NSMutableDictionary *)userInfo
{
ProgressTableViewCell* cell = [userInfo valueForKey:#"cell"];
NSNumber* progress = [userInfo valueForKey:#"percentage"];
[cell.progressView setProgress:progress.floatValue ];
NSLog(#"Progress after update: %f", cell.progressView.progress);
}
I am updating the progress view on the main thread and I have even tried setting waitUntilDone to YES but to no avail. My progress view stays at the zero point. Occasionally when I am debugging I can see some change in the progress indicator which makes me think it might be a timing problem. But how to solve it?
EDIT: Here is NSURLConnection delegate's didReceiveData method:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_responseData appendData:data];
NSNumber* bytes = [NSNumber numberWithUnsignedInt:[data length]];
NSLog(#"received bytes:%d", [bytes intValue] );
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
[userInfo setObject:_responseId forKey:#"responseId"];
[userInfo setObject:bytes forKey:#"receivedBytes"];
[self fireNotification: [NSNotification
notificationWithName:#"DidReceiveData"
object:self userInfo:userInfo]];
}
- (void)fireNotification :(NSNotification *)aNotification
{
[[NSNotificationCenter defaultCenter] postNotification:aNotification];
}
And here is my view controller's method that gets the notification:
-(void) dataReceived:(NSNotification *)notification {
NSNumber* responseId = [[notification userInfo] objectForKey:#"responseId"];
NSNumber* bytes = [[notification userInfo] objectForKey:#"receivedBytes"];
NSMutableDictionary* downloadInfo = [self getConnectionInfoForId:responseId];
NSLog(#"received bytes:%ld for response %#", [bytes longValue], responseId );
NSNumber* totalBytes = [NSNumber numberWithInt:([bytes longValue] + [[downloadInfo objectForKey:#"receivedBytes"] longValue]) ];
[downloadInfo setObject:totalBytes forKey:#"receivedBytes"];
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
[downloadInfo setObject:[NSNumber numberWithFloat:received/total] forKey:#"progress"];
[self reloadRowForResponse:responseId];
}
I have also added a nil check to my cellForRowAtIndexpath method as recommended:
ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:#"ProgressCell"];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProgressCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSNumber* percentage= [NSNumber numberWithFloat:received/total];
NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
NSLog(#"cell:%#", cell);
NSLog(#"percentage %f", percentage.floatValue);
[userInfo setObject:cell forKey:#"cell"];
[userInfo setObject:percentage forKey:#"percentage"];
[self performSelectorOnMainThread:#selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];
return cell;
I think you're taking the wrong approach by reloading the table cell every time the delegate method gets called. You can instead just grab the visible cell and update the progress indicator directly, rather than going through the data source.
I'm assuming you have some way of converting responseId to the index path of the row you want to update -- let's say that's called indexPathForResponseID: in your controller. Rather than reloading the cell, you can just grab the cell if it's visible and update its progress indicator:
- (void)dataReceived:(NSNotification *)notification {
...
float received = [[downloadInfo objectForKey:#"receivedBytes"] floatValue];
float total = [[downloadInfo objectForKey:#"totalFileSize"] floatValue];
NSIndexPath *cellPath = [self indexPathForResponseID:responseId];
ProgressTableViewCell *cell = (ProgressTableviewCell *)[self.tableView cellForRowAtIndexPath:cellPath];
if (cell) {
// make sure you're on the main thread to update UI
dispatch_async(dispatch_get_main_queue(), ^{
[cell.progressView setProgress: (received / total)];
}
}
}
You should be aware, though, that this solution won't suffice if you have more downloads than visible cells -- you should also be storing the progress of each download somewhere in your data source so that if the table view DOES need to reload a cell (due to a scroll), it knows how to set the progress indicator.
I've found in many cases like this that the cell you think you have isn't the one that's updating. When you reload, your code is popping a cell off the reusable, which is basically an old cell. If you reload, that cell gets replaced by another. (You also haven't included allocating a new cell if the reusable ones return nil) If your cell scrolls off, it gets reloaded, so you have to make sure that you're putting the progress bar on the cell that's there, not the one that used to be there. You might want to NSLog the address of the cell you started the transfer with, and then again each time you update the progress. You need to update the progress bar with the current cell at that index path. It may not be the one you initially started the process with.
Coming back to this almost 2 months later, I seem to find a solution. Not sure if this is good practice but creating a new UIProgressView each time the progress is updated seems to solve my problem. Here is the method:
-(void)updateProgressView :(NSMutableDictionary *)userInfo
{
ProgressTableViewCell* cell = [userInfo objectForKey:#"cell"];
cell.backgroundColor =[UIColor darkGrayColor];
NSNumber* progress = [userInfo objectForKey:#"percentage"];
NSLog(#"Progress before update: %f", cell.progressView.progress);
UIProgressView *pView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
pView.frame = CGRectMake(150, 200, 150, 9);
pView.progress = progress.floatValue;
[cell.contentView addSubview:pView];
}
Many thanks to everyone for their help.

Sort iPhone TableView From RSS Alphabetically

I am trying to change my podcast view from sorting via pubDate to sorting alphabetically. The code used in parsing the RSS currently for dates is:
NSMutableArray *entries = [NSMutableArray array];
[self parseFeed:doc.rootElement entries:entries];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
for (RSSEntryDirectory *entry in entries) {
int insertIdx = [_allEntries indexForInsertingObject:entry sortedUsingBlock:^(id a, id b) {
RSSEntryDirectory *entry1 = (RSSEntry *) a;
RSSEntryDirectory *entry2 = (RSSEntry *) b;
return [entry1.articleDate compare:entry2.articleDate];
}];
[_allEntries insertObject:entry atIndex:insertIdx];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:insertIdx inSection:0]]
withRowAnimation:UITableViewRowAnimationRight];
}
}];
How can I change this to just do alphabetically by one of the other entry properties?
I think you can replace the comparison line:
return [entry1.articleDate compare:entry2.articleDate];
by something like that (assuming it is NSString objects):
return [entry1.articleTitle localizedCaseInsensitiveCompare:entry2.articleTitle];

Can't understand why my tableview with GCD is crashing

I've just finished re-writing this, and covered every conceivable angle I can think of. I don't know why this is crashing. Perhaps somebody could help me figure it out.
This is the cellForRowAtIndexPath code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
JHomeViewCell *cell = (JHomeViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[JHomeViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.delegate = self;
}
cell.cellContent.cellInfo = [self cellInfoForCellAtIndexPath:indexPath];
if (cell.cellContent.cellInfo.thumbnailsComplete == YES || cell.cellContent.cellInfo.thumbnailsBeingCreated == YES) {
[cell.cellContent setNeedsDisplay];
}
else {
[cell.cellContent setup];
}
return cell;
}
And in cellContent, there's this setup method:
-(void)setup {
[self setNeedsDisplay];
self.cellInfo.thumbnailsBeingCreated = YES;
NSManagedObjectID *entryID = self.cellInfo.objectID;
dispatch_queue_t cellSetupQueue = dispatch_queue_create("com.Journalized.SetupCell", NULL);
dispatch_async(cellSetupQueue, ^{
NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *coordinator = [[CoreDataStore mainStore] context].persistentStoreCoordinator;
[newMoc setPersistentStoreCoordinator:coordinator];
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:newMoc];
Entry *entry = (Entry *)[newMoc objectWithID:entryID];
[newMoc save:nil];
int i = 0;
while (i < self.cellInfo.numberOfThumbnailsToDraw) {
NSLog(#"number of thumbnails: %i %i %i", self.cellInfo.numberOfThumbnailsToDraw, entry.media.count, i);
Media *media = [entry.media objectAtIndex:i];
UIImage *image = [media getThumbnail];
BOOL success = [newMoc save:nil];
//NSLog(#"time: %# success: %i", entry.entryTableInfo.creationTimeString, success);
[self.cellInfo.thumbnails setObject:image forKey:[NSNumber numberWithInt:i]];
i++;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
dispatch_async(dispatch_get_main_queue(), ^{
self.cellInfo.thumbnailsComplete = YES;
[self setNeedsDisplay];
});
});
dispatch_release(cellSetupQueue);
It crashes on the line:
Media *media = [entry.media objectAtIndex:i];
With the error:
index 1 beyond bounds [0 .. 0]
The NSLog above that...
NSLog(#"number of thumbnails: %i %i %i", self.cellInfo.numberOfThumbnailsToDraw, entry.media.count, i);
Gives the result:
number of thumbnails: 2 1 1
Which sort of explains the crash, except that value is set in the [cellInfoForCellAtIndexPath:]; method, like so:
cellInfo.numberOfMediaItems = entry.media.count;
cellInfo.numberOfThumbnailsToDraw = MIN(cellInfo.numberOfMediaItems, 3);
I really don't know where the problem is occurring, or why it's occurring, but I can't move on with my app until this part is fixed.
Well numberOfThumbnailsToDraw is 2 meaning the while loop will do 0, 1, but the count of your entry.media is only 1 so it only has a 0 index so of course it'll crash.
[managedObjectContext obtainPermanentIDsForObjects:self.cellInfo error:nil];
[managedObjectContext save:...];
NSManagedObjectID *entryID = self.cellInfo.objectID;
You need to make sure that
1. You have a permanent object ID; not a temporary one
2. The object is persisted so that it appears on the new MOC.
It looks like you are querying entry.media.count where entry is a pointer into one MOC, and then you are querying it from another. You are asking using the objectID, which is reasonable.
However, when the new MOC gets the object, it does not see the same values as you saw in the other MOC. Most likely, this means that you have not properly saved the other MOC.
What happens if you execute a fetch for the object on the new MOC?
Also, I would enable core data debugging (-com.apple.CoreData.SQLDebug 1) in command line options. This will log to the console what's going on underneath. You should see the SQL statements for the underlying database logged to the console.
Also, you are saving your MOC without ever making any changes to it, which leads me to believe you are a bit confused on how your many MOCs are working together.

UItableView load data on scroll

In my app I am getting the data from the web-service and I have to display it in UITableView.
But the condition here is I have to display only 10 records initially,then once the user scroll down I have to load more records.I tried searching it but didn't get any useful answer.
I agree that I will use -
(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
to display the value,but how will I fetch only 10 records from the service and then other record based on scroll.
Please provide some pointers or sample code.
Thanks
In case if some one need it,i was able to solve my problem this way.
First thing is you need the server configuration in such a way so that it should return 10 data at a time based on the row which is visible in TableView.
This is the tableView delegate which get called and returns the visible cells in tableView
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
int lastRow=[nameArray count]-1;
if(([indexPath row] == lastRow)&&(lastRow<[categoryArray count]))
{
if(tableView==m_pDetailsTableView) {
savedScrollPosition=lastRow;
startCellValue=[NSString stringWithFormat:#"%d",0];
endCellValue=[NSString stringWithFormat:#"%d",[nameArray count]+10];
[self connectToServer]; //Method to request to server to get more data
}
}
}
savedscrollPosition variable stores the variable as a point where you want to scroll the table view after load of data.
You should read about lazy loading. The code is available at Apple's website. Download it here
http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
check the code of
- (void)loadImagesForOnscreenRows
method.
It uses the same approach you need. It gets the current scroll position of the table view and on the basis of that, it will get the cells displayed on the screen and their indexPath. On the basis of that you will be able to show those cells which are shown in the screen.
For showing 10 rows, a simple calculation is required.
Just insert the new data into your datasource
see below
If you're using xml - check out XMLReader - turn XML into an NSDictionary
this sample code below uses AFNetworking (which is non blocking)
https://github.com/AFNetworking/AFNetworking/
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
[self fetchMoreData];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self fetchMoreData];
}
- (void)fetchMoreData
{
if ([resultArray count] > 0)
{
NSArray *visiblePaths = [myTableView indexPathsForVisibleRows];
NSIndexPath *lastRow = [visiblePaths lastObject];
// Check whether or not the very last row is visible.
NSInteger numberOfSections = [myTableView numberOfSections];
NSInteger lastRowSection = [lastRow section];
NSInteger lastRowRow = [lastRow row];
NSInteger numberOfRowsInSection = [myTableView numberOfRowsInSection:lastRowSection];
if (lastRowSection == numberOfSections - 1 &&
lastRowRow== numberOfRowsInSection - 1) {
DLog(#"it's the last row");
if ([resultArray count]%10 == 0) { // use a divider based on your pagination
[self fetchNextPage];
}
}
}
}
-(void)getFeeds{
ENTER_METHOD;
[resultArray removeAllObjects];
//reset this
NSString *url = [NSString stringWithFormat:#"/webserviceurl.xml?offset=0"];
[httpClient getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self parseFeedsXMLString:operation.responseString];
// offset = offset + 10; // ONLY if there's results increment
} failure:^(AFHTTPRequestOperation *operation, id responseObject){
NSString *detailError=nil;
}];
}
-(void)fetchNextPage{
NSString *url = [NSString stringWithFormat:#"/webserviceurl.xml?offset=%d",offset];
[httpClient getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
DLog(#"operation.responseString:%#",operation.responseString);
[self parseNextFeedsXMLString:operation.responseString];
// offset = offset + 10; // ONLY increment if there's results
} failure:^(AFHTTPRequestOperation *operation, id responseObject){
}];
}
- (void)parseFeedsXMLString:(NSString *)xmlString
{
NSError *parseError = nil;
NSDictionary *xmlDictionary = [XMLReader dictionaryForXMLString:xmlString error:&parseError];
DLog(#"xmlDictionary:%#",xmlDictionary);
resultArray = [[NSMutableArray arrayWithArray:[[xmlDictionary objectForKey:#"feed"] objectForKey:#"entry"]]retain];
[myTableView reloadData];
}
-(void)parseNextFeedsXMLString:(NSString *)xmlString
{
NSError *parseError = nil;
NSDictionary *xmlDictionary = [XMLReader dictionaryForXMLString:xmlString error:&parseError];
DLog(#"xmlDictionary:%#",xmlDictionary);
//[resultArray insertObject:e atIndex:[resultArray count]];
NSMutableArray *results = [NSMutableArray arrayWithArray:[[xmlDictionary objectForKey:#"feed"] objectForKey:#"entry"]];
if ([results count]) {
page++;
for (NSDictionary *dict in results) {
[resultArray insertObject:dict atIndex:[results count]];
}
}
[myTableView reloadData];
}
If I correctly understand your question ,you can do the following.
1 ) implement scrollViewDidScroll
2 ) check for visible rows in that
3 ) if you found the last row just call the web service for loading
more data
4 ) on getting the data just reload the table
Try it .
You can adjust the return value of tableView:numberOfRowsInSection: method, every time you want to insert ten rows, you can plus 10 to the return value.
sorry for my poor english.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger lastSectionIndex = [tableView numberOfSections] - 1;
NSInteger lastRowIndex = [tableView numberOfRowsInSection:lastSectionIndex] - 1;
if ((indexPath.section == lastSectionIndex) && (indexPath.row == lastRowIndex)) {
i = i +10;
NSString *unescaped = mySearchBar.text;
NSString *escapedString = [unescaped stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
[PServiceAPI searchKeyWord:escapedString withOffSet:i Handler:^(NSArray *results, NSError *error) {
if (error == nil) {
[arBase addObjectsFromArray:results];
[myTableView2 reloadData];
}
}];
}
}

Dealing with editable items in a filtered UITable with an NSMutableArray copy

I have a UITable with a datasource that is set to a 'copy' of the original data. I use this copy to displayed filtered results (either 'All' or only those that are 'checked' with a UISwitch in each row). My logic for doing the filtering is here:
-(void)filterItems {
[tableData removeAllObjects];
if (checkedOrNotSwitch.selectedSegmentIndex == 0) {
[tableData addObjectsFromArray:self.defaultChecklistItemsArray];
} else {
for (NSMutableDictionary *sectionDict in self.defaultChecklistItemsArray) {
NSMutableArray *newItemsArray = [[NSMutableArray alloc] init];
[newItemsArray removeAllObjects];
for (NSMutableDictionary *itemDict in [sectionDict objectForKey:#"categoryItems"]) {
if ([[itemDict objectForKey:#"isComplete"] boolValue]) {
[newItemsArray addObject:itemDict];
}
}
if ([newItemsArray count] > 0) {
NSMutableDictionary *newSectionDict = [[NSMutableDictionary alloc] initWithDictionary:sectionDict];
[newSectionDict setObject:newItemsArray forKey:#"categoryItems"];
[tableData addObject:newSectionDict];
[newSectionDict release];
}
}
}
[checklistTable reloadData];
}
The filtering itself now works correctly. In my custom cell, each row has a UISwitch. The switch runs this function when its changed:
-(IBAction) switchValueChanged{
NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];
[self.parentController updateCompletedStatusAtIndexPath:indexPath toStatus:isCompleted.on];
}
The function above is in the class for the tableviewcell itself. The function I call in the superview is this:
-(void)updateCompletedStatusAtIndexPath:(NSIndexPath *)indexPath toStatus:(BOOL)status{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSMutableDictionary *currentsection = [[NSMutableDictionary alloc] initWithDictionary:[tableData objectAtIndex:section]];
NSMutableArray *itemsArray = [[NSMutableArray alloc] initWithArray:[currentsection objectForKey:#"categoryItems"] copyItems:YES];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] initWithDictionary:[itemsArray objectAtIndex:row]];
NSLog(#"BOOL = %#\n", (status ? #"YES" : #"NO"));
[tempDict setValue:[NSNumber numberWithBool:status] forKey:#"isComplete"];
[itemsArray replaceObjectAtIndex:row withObject:tempDict];
[currentsection setValue:itemsArray forKey:#"categoryItems"];
[tableData replaceObjectAtIndex:section withObject:currentsection];
[tempDict release];
[itemsArray release];
[currentsection release];
[checklistTable reloadData];
}
Before I implemented the filtering and used the logic above on self.defaultChecklistItemsArray directly, it worked and saved the data when the switch was flipped.
Now when I first enter the app, it loads the array of data from nsuserdefaults. I navigate to the view with the table and it displays the data correctly there with the UISwitches all in the correct position given the data (some on, some off). When I tap one of the switches, then click the segmentedcontrol that does the filtering, the data reverts back to the state it was loaded in, implying that the flipping of the switch did not actually effect the data at all (even though I don't think I did a deep copy anywhere here so I figured it should be doing the right thing). Any suggestions?