Синхронизация данных в UITableView с TWRequest в iOS5 - PullRequest
1 голос
/ 23 ноября 2011

Я пытался работать с TWrequest и получить блок твитов, но у меня возникают проблемы с синхронизацией данных и обновлением таблицы.

У меня есть следующий относительно простой блок в TableViewController для получения информации об учетной записи пользователя:

- (void)viewDidLoad
{
  NSArray *twitterAccounts = [[NSArray alloc] init];    
  if ([TWRequest class]) {        
    store = [[ACAccountStore alloc] init];
    ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

    [store requestAccessToAccountsWithType:twitterType withCompletionHandler:^(BOOL granted, NSError *error){
        if (granted == YES) {
            NSLog(@"Access granted to Twitter Accounts");
        } else {
            NSLog(@"Access denied to Twitter Accounts");
        }
    }];       
    twitterAccounts = [store accountsWithAccountType:twitterType];
    self.account = [[store accounts] objectAtIndex:0];
  } else {
    // The iOS5 Twitter Framework is not supported so need to provide alternative
  }

  self.homeTimeLineData = [NSMutableArray array];
  self.profileImages = [NSMutableDictionary dictionary];
  [self updateTimeLineData:HOME_TIMELINE_URL];
  [super viewDidLoad];
}

получить последние 20 твитов из домашней хроники пользователей:

- (void)updateTimeLineData:(NSString *)urlString {

  NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:@"20", @"count", nil];    
  TWRequest *myHomeTimeLine = [[TWRequest alloc] initWithURL:[NSURL URLWithString:urlString] parameters:parameters requestMethod:TWRequestMethodGET];
  myHomeTimeLine.account = account;

  [myHomeTimeLine performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {    
    NSError *jsonError = nil;
    NSArray *timeLineData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self refresh:timeLineData]; 
    }); 
  }];    
}

парсинг данных временной шкалы и перезагрузка таблицы:

- (void)refresh:(NSArray *)arrayOfTweets {

  for (int i=0; i < [arrayOfTweets count]; i++) {       
    NSDictionary *rawTweetJSON = [arrayOfTweets objectAtIndex:i];

    Tweet *tweet = [[Tweet alloc] init];
    tweet.screenName = [[rawTweetJSON objectForKey:@"user"] objectForKey:@"screen_name"];
    tweet.tweetText = [rawTweetJSON objectForKey:@"text"];
    tweet.createdAt = [rawTweetJSON objectForKey:@"created_at"];

    [self.homeTimeLineData insertObject:tweet atIndex:i];
  }

  [self.tableView reloadData];    
}

Это работает нормально, но каждый раз, когда я пытаюсь изменить это, я сталкиваюсь с проблемами

  1. Если я попытаюсь проанализировать дату JSON в блоке executeRequestWithHandler и передать обратно проанализированные данные, я получу ошибку подтверждения.
  2. Если я перенесу запрос на доступ к учетным записям в AppDelegate (чтобы в конечном итоге информация об учетных записях могла быть разделена между различными контроллерами представления) и передал нужную учетную запись в TableViewController, я получу ошибку подтверждения.

В обоих случаях ошибка подтверждения выглядит одинаково: просмотр таблицы обновит достаточно твитов, чтобы заполнить экран в первый раз, затем вызовет tableView: numberOfRowsInSection: и после того, как данные для следующей ячейки будут нулевыми.

2011-11-23 00: 02: 57.470 TwitteriOS5Tutorial [9750: 10403] * Ошибка подтверждения в - [UITableView _createPreparedCellForGlobalRow: withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1912.3/UIT : 6072

2011-11-23 00: 02: 57.471 TwitteriOS5Tutorial [9750: 10403] * Завершение работы приложения из-за невыполненной исключительной ситуации NSInternalInconsistencyException, причина: «Источник данных UITableView должен возвращать ячейку из tableView: cellForRowAtIndexPath: «

Я пытался рассмотреть другие подобные вопросы, но не мог понять, как синхронизировать различные блоки. Что показалось мне странным, так это влияние того, где находится запрос информации об учетной записи.

homeTimeLineData в данном случае является просто NSMutableArray. Является ли решение перейти к Core Data и использовать NSFetchResultsController?

Мои знания о блоках все еще немного ограничены, поэтому любые знания будут высоко оценены. Спасибо, Norman


Обновление:
Я считаю, что нашел проблему, она связана с раскадровками и TableViewCells.

В учебном пособии по раскадровке я сказал, что вы можете заменить

NSString *CellIdentifier = @"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
    cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

с одной строки

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCell"];

и раскадровка позаботится о создании новой копии ячейки автоматически, если ее не будет в очереди. Я проверил документацию Apple, и там тоже написано.

Я последовал этому совету и использовал только однострочное объявление, и оно работало до определенного момента, но затем, когда я выполнял рефакторинг и реорганизацию кода, я начал получать ошибки утверждения.

Я использую пользовательский UITableViewCell с IBOutlets для изменения положения различных элементов в ячейке, поэтому может быть ограничением то, что для удаления строк, которые «проверяют возвращаемое значение ячейки», вы должны придерживаться стандартных ячеек и делать все ваши настройка в раскадровке и Интерфейсном Разработчике.

Кто-нибудь сталкивался с подобной проблемой?

1 Ответ

3 голосов
/ 12 декабря 2011

Я полагаю, вы используете ARC.Убедитесь, что вы удерживаете AccountStore, например, объявив сильное свойство, подобное этому:

// AccountsListViewController.h
@property (strong, nonatomic) ACAccountStore *accountStore;

Затем вы можете использовать этот список для получения списка учетных записей на вашем устройстве:

// AccountsListViewController.m
- (void)fetchData
{
    if (_accounts == nil) {
        if (_accountStore == nil) {
            self.accountStore = [[ACAccountStore alloc] init];
        }
        ACAccountType *accountTypeTwitter = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
        [self.accountStore requestAccessToAccountsWithType:accountTypeTwitter withCompletionHandler:^(BOOL granted, NSError *error) {
            if(granted) {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    self.accounts = [self.accountStore accountsWithAccountType:accountTypeTwitter];
                    [self.tableView reloadData]; 
                });
            }
        }];
    }
}

Как только пользователь выберет одну из учетных записей, назначьте ее контроллеру представления, который вы будете использовать для отображения списка твитов:

// AccountsListViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    TweetsListViewController *tweetsListViewController = [[TweetsListViewController alloc] init];
    tweetsListViewController.account = [self.accounts objectAtIndex:[indexPath row]];
    [self.navigationController pushViewController:tweetsListViewController animated:TRUE];
}

Теперь в TweetListViewController вам нужнодержитесь за учетную запись в сильном свойстве, вот так:

// TweetListViewController.h
@property (strong, nonatomic) ACAccount *account;
@property (strong, nonatomic) id timeline;

И, наконец, извлекайте твиты в своем представлении следующим образом:

// TweetListViewController.m
- (void)fetchData
{
    NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1/statuses/home_timeline.json"];
    TWRequest *request = [[TWRequest alloc] initWithURL:url 
                                                 parameters:nil 
                                              requestMethod:TWRequestMethodGET];
    [request setAccount:self.account];    
    [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        if ([urlResponse statusCode] == 200) {
            NSError *jsonError = nil;

            self.timeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];
            dispatch_sync(dispatch_get_main_queue(), ^{
                [self.tableView reloadData];
            });
        }
    }];

}

Парсинг данных JSON внутри performRequestWithHandler совсем не плохая идея.На самом деле, я не вижу, что должно быть не так с этим.Вы работаете в хорошей асинхронной манере, используя GCD, чтобы вернуться в основной поток - это прекрасно!

Полное обсуждение источника, который я разместил, можно найти здесь: http://www.peterfriese.de/the-accounts-and-twitter-framework-on-ios-5/,источник доступен на Github: https://github.com/peterfriese/TwitterClient

...