Asynchronous loading - UITableView and Firebase

前端 未结 2 1505
庸人自扰
庸人自扰 2021-01-07 06:28

I\'m working on a project that loads list data from Firebase and populates a UITableView. While I see the snapshots being called from my firebase instance, they don\'t popul

相关标签:
2条回答
  • 2021-01-07 07:02

    Figured it out. Rather then trying to query the database in viewDidLoad, in which case the view already came up as a frame, I moved the query to the time the nib was loaded but before the frame became visible. Here's the code, expanded upon:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        //link the CloudViewController with its View
        NSBundle *appBundle = [NSBundle mainBundle];
    
        self = [super initWithNibName:@"FoodTruckViewController" bundle:appBundle];
        if (self) {
            self.sampleTableView.delegate = self;
            self.sampleTableView.dataSource = self;
            [self loadDataFromFirebase];
            [self.sampleTableView reloadData];
        }
        return self;
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        [self.sampleTableView reloadData];
    }
    
    -(void)loadDataFromFirebase
    {
        handles = [[NSMutableArray alloc] init];
        Firebase* listRef = [[Firebase alloc] initWithUrl:@"https://wheresthatfoodtruck.firebaseIO.com/foodtrucks"];
        [listRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
            NSLog(@"%@", snapshot.value);
            [handles addObject:snapshot.value];
        }];
    }
    
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return [handles count];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }
    [cell.textLabel setText:[handles objectAtIndex:indexPath.row]];
    
        return cell;
    }
    
    #pragma mark - Table view delegate
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // Navigation logic may go here. Create and push another view controller.
        /*
         <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
         // ...
         // Pass the selected object to the new view controller.
         [self.navigationController pushViewController:detailViewController animated:YES];
         */
    }
    
    @end
    

    This version of the code populates the table before it becomes visible, but won't update from changes to the table in real time. I think the next step will be to make a call to the ChildChanged function form firebase in the viewDidLoad method to keep updates of any changes on the firebase side.

    0 讨论(0)
  • 2021-01-07 07:19

    With Firebase you have to keep in mind that all code (and, in particular, the blocks for the realtime updates) is asynchronously. For example, in the original question's comments as well as in the work-around answer, code like this:

    [self loadDataFromFirebase];
    [self.sampleTableView reloadData];
    

    ... can't be read from top to bottom; there will be a race condition between the data being populated (or views being updated) in the asynchronous blocks and the reloadData being called; maybe the cell connection is spotty, or GCD is optimizing for one thing over the other, and so on.

    The second thing to keep in mind with Firebase is that for events like child_added there might not ever be any notion of "complete" — it's entirely possible for data to continually be shifting and updating; again, when would one expect reloadData to get called? Right away? After the first child? Last child? The implications of this are that if new data is added, even the work-around is going to yield behavior you're probably not expecting (i.e. it'll appear as if data isn't showing up because reloadData will never get called again.)

    One approach you can take here would be to directly update your views as the data changes; so, your block would then look like:

    [listRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
      NSLog(@"%@", snapshot.value);
      [handles addObject:snapshot.value];
      [self.sampleTableView reloadData];
    }];
    

    Another approach might be to use Firebase to exclusively update your internal models and use the regular Objective-C approaches (KVO, Notifications, MVC, etc) to separate your data and view concerns to rerender views outside of direct changes to your models.

    0 讨论(0)
提交回复
热议问题