Search code examples
objective-cuitableviewfirebasedata-retrieval

Populating Tableviews with Images in Firebase


I recently asked this question: Populating a UITableView in Firebase a Set Number of Cells at a time

But through the discussion I realized that the question was actually two questions, so I'm going to start a second thread for the second question:

Is there a way people generally populate tableviews of images from large data sets using Firebase?

Below is the code I'm using for retrieving images. The images are base64 encoded NSStrings.

NSString* profPicString= [child.value objectForKey: @"profilePicture"];
    NSData *dataFromBase64=[NSData base64DataFromString:profPicString];
    UIImage *profPicImage = [[UIImage alloc]initWithData:dataFromBase64];
    item.profilePicture = profPicImage;

The images, however, take a long time to download. A tableview of just 10 images will take 5 or 10 seconds to load. If it's like this with 10 images, it will be truly glacial with hundreds or thousands.

I doubt I'm the first person to encounter this issue, and I was wondering if there's a better/more efficient way of doing it?

Thanks for the help!


Solution

  • If you're going to store your images as base64 encoded strings, store them in a separate location in the Firebase database.

    {
       "pokemon": {
          "001": {
             "name": "Bulbasaur"
          },
          "002": {
             "name": "IvySaur"
          }
       },
       "images": {
          "001": "big-ole-base64-string",
          "002": "big-ole-base64-string"
       }
    }
    

    This way you can load the main data without needed to pull down the images.

    Now when you load your data for your UITableView, create a listener for the main set of data, /pokemon in this case.

    Then when you get to tableView(_:cellForRowAtIndexPath), create a one-time listener to grab the image.

    This example is in Swift, but you can convert to Objective-C.

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCellWithIdentifier("PokeCell") as! PokeTableViewCell
    
      let pokeSnap = pokemonSnaps[indexPath.item]
    
      let imageRef = rootRef.childByAppendingPath("images").childByAppendingPath(pokeSnap.key)
    
      imageRef.observeSingleEventOfType(.Value) { (imageSnap: FDataSnapshot!) in
        let base64String = imageSnap.value as! String
        let decodedData = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions(rawValue: 0))
        let image = UIImage(data: decodedData!)
        cell.pokeImage.image = image
      }
    
      return cell   
    }
    

    The beauty of this method is that you only load the images when needed, not with the main set of data.

    Check out this repo of mine that implements this technique.