{"id":1431,"date":"2022-08-30T15:16:28","date_gmt":"2022-08-30T15:16:28","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2013\/11\/16\/synchronizing-data-in-uitableview-with-twrequest-in-ios5-collection-of-common-programming-errors\/"},"modified":"2022-08-30T15:16:28","modified_gmt":"2022-08-30T15:16:28","slug":"synchronizing-data-in-uitableview-with-twrequest-in-ios5-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2022\/08\/30\/synchronizing-data-in-uitableview-with-twrequest-in-ios5-collection-of-common-programming-errors\/","title":{"rendered":"Synchronizing data in UITableView with TWRequest in iOS5-Collection of common programming errors"},"content":{"rendered":"<p>I&#8217;ve been trying to work with TWrequest and retrieve a block of Tweets but I&#8217;m running into problems keeping the data synchronized and update the tableview.<\/p>\n<p>I have the following relatively simple block in the TableViewController to get the users account information :<\/p>\n<pre><code>- (void)viewDidLoad\n{\n  NSArray *twitterAccounts = [[NSArray alloc] init];    \n  if ([TWRequest class]) {        \n    store = [[ACAccountStore alloc] init];\n    ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];\n\n    [store requestAccessToAccountsWithType:twitterType withCompletionHandler:^(BOOL granted, NSError *error){\n        if (granted == YES) {\n            NSLog(@\"Access granted to Twitter Accounts\");\n        } else {\n            NSLog(@\"Access denied to Twitter Accounts\");\n        }\n    }];       \n    twitterAccounts = [store accountsWithAccountType:twitterType];\n    self.account = [[store accounts] objectAtIndex:0];\n  } else {\n    \/\/ The iOS5 Twitter Framework is not supported so need to provide alternative\n  }\n\n  self.homeTimeLineData = [NSMutableArray array];\n  self.profileImages = [NSMutableDictionary dictionary];\n  [self updateTimeLineData:HOME_TIMELINE_URL];\n  [super viewDidLoad];\n}\n<\/code><\/pre>\n<p>fetch the latest 20 tweets from the users home timeline:<\/p>\n<pre><code>- (void)updateTimeLineData:(NSString *)urlString {\n\n  NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:@\"20\", @\"count\", nil];    \n  TWRequest *myHomeTimeLine = [[TWRequest alloc] initWithURL:[NSURL URLWithString:urlString] parameters:parameters requestMethod:TWRequestMethodGET];\n  myHomeTimeLine.account = account;\n\n  [myHomeTimeLine performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {    \n    NSError *jsonError = nil;\n    NSArray *timeLineData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&amp;jsonError];\n    dispatch_async(dispatch_get_main_queue(), ^{\n        [self refresh:timeLineData]; \n    }); \n  }];    \n}\n<\/code><\/pre>\n<p>parse the timeline data and reload the tableview :<\/p>\n<pre><code>- (void)refresh:(NSArray *)arrayOfTweets {\n\n  for (int i=0; i &lt; [arrayOfTweets count]; i++) {       \n    NSDictionary *rawTweetJSON = [arrayOfTweets objectAtIndex:i];\n\n    Tweet *tweet = [[Tweet alloc] init];\n    tweet.screenName = [[rawTweetJSON objectForKey:@\"user\"] objectForKey:@\"screen_name\"];\n    tweet.tweetText = [rawTweetJSON objectForKey:@\"text\"];\n    tweet.createdAt = [rawTweetJSON objectForKey:@\"created_at\"];\n\n    [self.homeTimeLineData insertObject:tweet atIndex:i];\n  }\n\n  [self.tableView reloadData];    \n}\n<\/code><\/pre>\n<p>This works fine but each time I try to make a variation on this I run into problems<\/p>\n<ol>\n<li>If I try to parse the JSON date in the performRequestWithHandler block and pass back the parsed data, I get an assertion failure.<\/li>\n<li>If I move the request for access to the accounts to the AppDelegate (so that the accounts information can eventually be shared between different view controllers) and pass the account I need to the TableViewController, I get an assertion failure.<\/li>\n<\/ol>\n<p>In both cases the assertion failure appears to be the same : the tableview will update enough tweets to fill the screen the first time then it will make a call to tableView:numberOfRowsInSection: and after the data for the next cell is null.<\/p>\n<p>2011-11-23 00:02:57.470 TwitteriOS5Tutorial[9750:10403] <em>*<\/em> Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], \/SourceCache\/UIKit_Sim\/UIKit-1912.3\/UITableView.m:6072<\/p>\n<p>2011-11-23 00:02:57.471 TwitteriOS5Tutorial[9750:10403] <em>*<\/em> Terminating app due to uncaught exception &#8216;NSInternalInconsistencyException&#8217;, reason: &#8216;UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:&#8217;<\/p>\n<p>I&#8217;ve tried looking at other similar questions but I couldn&#8217;t understand how to keep the different blocks in sync. What struck me as odd is the impact of where the request for account information is located.<\/p>\n<p>homeTimeLineData in this case is just an NSMutableArray. Is the solution to go to Core Data and use an NSFetchResultsController?<\/p>\n<p>My knowledge of blocks is still a little limited so any insights would be greatly appreciated. Thanks, Norman<\/p>\n<p>Update :<\/p>\n<p>I believe I found the problem, it is related to Storyboards and TableViewCells.<\/p>\n<p>In a tutorial on Storyboards I followed it said you could replace<\/p>\n<pre><code>NSString *CellIdentifier = @\"MyCell\";\nUITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];\n\nif (cell == nil) {\n    cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];\n}\n<\/code><\/pre>\n<p>with a single line<\/p>\n<pre><code>UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@\"MyCell\"];\n<\/code><\/pre>\n<p>and the Storyboard would take care of creating a new copy of the cell automatically if there was not one to dequeue. I checked the Apple documentation and it says this also.<\/p>\n<p>I followed this advice and used just the single line declaration and it worked up to a point but then as I was refactoring and rearranging the code I started to get the assertion failures.<\/p>\n<p>I am using a custom UITableViewCell with IBOutlets to reposition different elements within the cell so it may be a limitation that to remove the lines that &#8220;check the return value of the cell&#8221; you have to stick with standard cells and do all your customization within the storyboard and Interface Builder.<\/p>\n<p>Has anyone run into a similar problem?<\/p>\n<ol>\n<li>\n<p>I guess you&#8217;re using ARC. Make sure you hold on to the <code>AccountStore<\/code>, e.g. by declaring a strong property like this:<\/p>\n<pre><code>\/\/ AccountsListViewController.h\n@property (strong, nonatomic) ACAccountStore *accountStore;\n<\/code><\/pre>\n<p>You can then use this list to fetch the list of accounts on your device:<\/p>\n<pre><code>\/\/ AccountsListViewController.m\n- (void)fetchData\n{\n    if (_accounts == nil) {\n        if (_accountStore == nil) {\n            self.accountStore = [[ACAccountStore alloc] init];\n        }\n        ACAccountType *accountTypeTwitter = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];\n        [self.accountStore requestAccessToAccountsWithType:accountTypeTwitter withCompletionHandler:^(BOOL granted, NSError *error) {\n            if(granted) {\n                dispatch_sync(dispatch_get_main_queue(), ^{\n                    self.accounts = [self.accountStore accountsWithAccountType:accountTypeTwitter];\n                    [self.tableView reloadData]; \n                });\n            }\n        }];\n    }\n}\n<\/code><\/pre>\n<p>As soon as the user selects one of the accounts, assign it to the view controller you&#8217;ll be using to display the list of tweets:<\/p>\n<pre><code>\/\/ AccountsListViewController.m\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath\n{\n    TweetsListViewController *tweetsListViewController = [[TweetsListViewController alloc] init];\n    tweetsListViewController.account = [self.accounts objectAtIndex:[indexPath row]];\n    [self.navigationController pushViewController:tweetsListViewController animated:TRUE];\n}\n<\/code><\/pre>\n<p>Now, in TweetListViewController, you need to hold on to the account in a strong property, just like this:<\/p>\n<pre><code>\/\/ TweetListViewController.h\n@property (strong, nonatomic) ACAccount *account;\n@property (strong, nonatomic) id timeline;\n<\/code><\/pre>\n<p>And finally fetch the tweets in your view like this:<\/p>\n<pre><code>\/\/ TweetListViewController.m\n- (void)fetchData\n{\n    NSURL *url = [NSURL URLWithString:@\"https:\/\/api.twitter.com\/1\/statuses\/home_timeline.json\"];\n    TWRequest *request = [[TWRequest alloc] initWithURL:url \n                                                 parameters:nil \n                                              requestMethod:TWRequestMethodGET];\n    [request setAccount:self.account];    \n    [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {\n        if ([urlResponse statusCode] == 200) {\n            NSError *jsonError = nil;\n\n            self.timeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&amp;jsonError];\n            dispatch_sync(dispatch_get_main_queue(), ^{\n                [self.tableView reloadData];\n            });\n        }\n    }];\n\n}\n<\/code><\/pre>\n<p>Parsing the JSON data inside <code>performRequestWithHandler<\/code> isn&#8217;t a bad idea at all. In fact, I don&#8217;t see what should be wrong with doing this. You&#8217;re working in a nice asynchronous manner, making use of GCD to get back on the main thread &#8211; that&#8217;s fine!<\/p>\n<p>A complete discussion of the source I posted can be found here: http:\/\/www.peterfriese.de\/the-accounts-and-twitter-framework-on-ios-5\/, the source is available on Github: https:\/\/github.com\/peterfriese\/TwitterClient<\/p>\n<\/li>\n<\/ol>\n<p id=\"rop\"><small>Originally posted 2013-11-16 20:51:10. <\/small><\/p>","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been trying to work with TWrequest and retrieve a block of Tweets but I&#8217;m running into problems keeping the data synchronized and update the tableview. I have the following relatively simple block in the TableViewController to get the users account information : &#8211; (void)viewDidLoad { NSArray *twitterAccounts = [[NSArray alloc] init]; if ([TWRequest class]) [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1431","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/1431","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=1431"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/1431\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=1431"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=1431"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=1431"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}