0
votes

I am trying to delete a row from table view. I have an array that holds objects to display (Articles). I do everything as I was told in tutorials. I delete row, then delete respective object form array, but always get an execution. I have tried solutions for similar cases, but they could not solve the problem. I am stuck with this problem already 3rd day. Hope you guys can help. Thank you. I will add any additional information or code as needed.

EDIT: I might have found the cause of the problem. I import new articles at each login, from external server. At each login, I send date of lastly added article, and in response I get all articles that are not in my database. You will see it in the code. I guess that self.article gets updated with received data, only after row deletion has been done. I might be wrong (most likely :) ). Hope this helps at all.

InboxViewController.m

    //
//  ArticleViewController.m
//  ReadLater
//
//  Created by Ibragim Gapuraev on 09/06/2014.
//  Copyright (c) 2014 Sermilion. All rights reserved.
//

#import "InboxViewController.h"
#import "LoginViewController.h"

@interface InboxViewController ()

@end

@implementation InboxViewController

@synthesize db,articles, response, jsonData;


- (NSMutableArray* ) articles
{
    if (!articles) {
        articles = [[NSMutableArray alloc] initWithCapacity:20];
    }

    return articles;
}

- (Database* ) db
{
    if (!db) {
        db = [[Database alloc] init];
    }
    return db;
}


- (void)setInboxList:(NSMutableArray* )inboxList
{
    self.articles = inboxList;
}

//---------------------------------Getting data from web-------------------------------------------//

/**
 The method will connect to a given url and send date of article that has been added at last.
 Then, connectionDidFinishLoading will receive json array of all articles, that have been added to server database after that time
 **/
- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost/nextril/index.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];

    NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    [self.db openDatabase];
    NSString* date_added = [self.db getLastArticleDate];
    [self.db closeDatabase];

    //send id of article that was added last, to server,
    //which will return json arrya of all articles with id greater then the one sent
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[date_added dataUsingEncoding:NSUTF8StringEncoding]];

    if (connection) {
        NSLog(@"viewWillAppear: Connecting to server to get data...");
    }else{
        NSLog(@"viewWillAppear: Error while connecting...");
    }


}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data
{
    response = [[NSData alloc] initWithData:data];
}

//Check if data been received
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    if(sizeof(response)>0){
        //NSLog(@"Got response from server %@", response);
        NSError* error;
        NSArray* json = [NSJSONSerialization
                              JSONObjectWithData:response //1
                              options:kNilOptions
                              error:&error];

        self.jsonData = [[NSMutableArray alloc] initWithArray:json];
        int count = 0;
        [self.db openDatabase];

        for (int i=0; i<self.jsonData.count; i++) {

            NSDictionary *item = [self.jsonData objectAtIndex:i];
            NSInteger article_id = [[item objectForKey:@"article_id"]integerValue];
            NSString* content = [item objectForKey:@"content"];
            NSString* author = [item objectForKey:@"author"];
            NSString* date = [item objectForKey:@"date"];

            NSString* url = [item objectForKey:@"url"];
            NSString* tags = [item objectForKey:@"tags"];
            NSInteger archived = [[item objectForKey:@"archived"]integerValue];
            NSString* title = [item objectForKey:@"title"];
            //NSLog(@"",);
            Article* article = [[Article alloc]initWithId:article_id content:content author:author date:date url:url tags:tags arhived:archived title:title];

            BOOL added = [self.db addArticleToLocalDatabase:article];
            BOOL addedToUser = [self.db addArticleToInbox:article];
            [self.articles addObject:article];
            count++;
            if (added == true && addedToUser == true) {
                NSLog(@"connectionDidFinishLoading: Articles has been imported. Size: %d %@", self.articles.count, self.articles);
            }else{
                NSLog(@"connectionDidFinishLoading: Failed to import article.");
            }
        }
        [self.db closeDatabase];
    }else{
        NSLog(@"connectionDidFinishLoading: Did not get resopnse from server: %@", response);
    }
    connection = nil;
}
//----------------------------------------------------------------------------------------------------



- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.db openDatabase];
    self.articles = [self.db importAllInboxArticles:16];
    [self.db closeDatabase];

    self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:self.navigationItem.rightBarButtonItem,self.editButtonItem, nil];


    //NSLog(@"Number of articles in inboxArticles %d", articles.count);
    // Do any additional setup after loading the view.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return self.articles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{



    //NSLog(@"Number of articles in articles %d", self.articles.count);
    static NSString *CellIdentifier = @"Content";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...
    Article* article = [self.articles objectAtIndex:indexPath.row];
    NSString *listingKey = article.title;
    NSString *listingValues = article.url;
    cell.textLabel.text = listingKey;
    cell.detailTextLabel.text = listingValues ;

    return cell;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        NSLog(@"Size of artilces: %d", self.articles.count);

        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [self.articles removeObjectAtIndex:indexPath.row];

        [tableView endUpdates];
        [tableView reloadRowsAtIndexPaths:(NSArray *)indexPath withRowAnimation:UITableViewRowAnimationFade];

    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}





@end

Stack Trace:

2014-06-17 11:07:49.131 ReadLater[76572:60b] connectionDidFinishLoading: Articles has been imported. Size: 4 ( "", "", "", "" ) 2014-06-17 11:07:51.078 ReadLater[76572:60b] Size of artilces: 4 2014-06-17 11:07:51.078 ReadLater[76572:60b] * Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2935.137/UITableView.m:1368 2014-06-17 11:07:51.081 ReadLater[76572:60b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' * First throw call stack: ( 0 CoreFoundation 0x018e61e4 exceptionPreprocess + 180 1 libobjc.A.dylib
0x016658e5 objc_exception_throw + 44 2 CoreFoundation
0x018e6048 +[NSException raise:format:arguments:] + 136 3
Foundation 0x012454de -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 116 4
UIKit 0x00313f63 -[UITableView _endCellAnimationsWithContext:] + 13402 5 UIKit 0x00323cea -[UITableView endUpdatesWithContext:] + 51 6 UIKit
0x00323d18 -[UITableView endUpdates] + 41 7 ReadLater
0x00005044 -[InboxViewController tableView:commitEditingStyle:forRowAtIndexPath:] + 484 8 UIKit
0x003356a3 -[UITableView animateDeletionOfRowWithCell:] + 107 9
UIKit 0x004be595 -[UITableViewCell _swipeDeleteButtonPushed] + 70 10 libobjc.A.dylib 0x01677880 -[NSObject performSelector:withObject:withObject:] + 77 11 UIKit 0x0023d3b9 -[UIApplication sendAction:to:from:forEvent:] + 108 12 UIKit
0x0023d345 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61 13 UIKit 0x0033ebd1 -[UIControl sendAction:to:forEvent:] + 66 14 UIKit
0x0033efc6 -[UIControl _sendActionsForEvents:withEvent:] + 577 15 UIKit 0x0033e243 -[UIControl touchesEnded:withEvent:] + 641 16 UIKit
0x005d32e3 _UIGestureRecognizerUpdate + 7166 17 UIKit
0x0027ca5a -[UIWindow _sendGesturesForEvent:] + 1291 18 UIKit
0x0027d971 -[UIWindow sendEvent:] + 1021 19 UIKit
0x0024f5f2 -[UIApplication sendEvent:] + 242 20 UIKit
0x00239353 _UIApplicationHandleEventQueue + 11455 21 CoreFoundation 0x0186f77f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
+ 15 22 CoreFoundation 0x0186f10b __CFRunLoopDoSources0 + 235 23 CoreFoundation 0x0188c1ae __CFRunLoopRun + 910 24 CoreFoundation
0x0188b9d3 CFRunLoopRunSpecific + 467 25 CoreFoundation
0x0188b7eb CFRunLoopRunInMode + 123 26 GraphicsServices
0x038da5ee GSEventRunModal + 192 27 GraphicsServices
0x038da42b GSEventRun + 104 28 UIKit
0x0023bf9b UIApplicationMain + 1225 29 ReadLater
0x0000858d main + 141 30 libdyld.dylib
0x01f2d701 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)

2
You need to update the data source before you reload the tableview row - CW0007007
If you look at the stack trace you will see that your exception occurred in InboxViewController, not ArticleViewController. You should set an exception breakpoint to find the place where the crash is occurring and post the correct code snippet - Paulw11
Oh, my bad. Yeah, it is InboxViewController. Sorry about that. - sermilion
I see from the NSLogs that there are initially 4 entries in the articles array, but the exception states that there are 3. It looks like something else is deleting an element from the array? - Paulw11
NSMutable array is not thread safe. Once you have retrieved your data you should use a gcd dispatch block to perform your array updates on the main thread. - Paulw11

2 Answers

1
votes

I made it work!) I have set breakpoints and examined what's going on. I took advice of @Paulw11 and made my self.articles be immutable and created mutable copy of it. Then, I decided to remove line that reloads view(row), and everything started working.

Working code:

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return safeArticles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    //NSLog(@"Number of articles in articles %d", self.articles.count);
    static NSString *CellIdentifier = @"Content";
    //UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    SHCTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.textLabel.backgroundColor = [UIColor clearColor];

    // Configure the cell...
    Article* article = [safeArticles objectAtIndex:indexPath.row];
    NSString *listingKey = article.title;
    NSString *listingValues = article.url;
    cell.textLabel.text = listingKey;
    cell.detailTextLabel.text = listingValues ;
    cell.delegate = self;
    cell.todoItem = article;
    return cell;
}

//// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        NSLog(@"Size of artilces: %d", safeArticles.count);

        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [safeArticles removeObjectAtIndex:indexPath.row];

        [tableView endUpdates];

        ///[tableView reloadRowsAtIndexPaths:(NSArray *)indexPath withRowAnimation:UITableViewRowAnimationFade];

    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
-1
votes

just reload those rows rather than the whole table using:

 - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:   (UITableViewRowAnimation)animation