Thursday Tech Tip – Firebase & Image Handling

9 Jan , 2014 Technology,Thursday Tech Tips

Thursday Tech Tip – Firebase & Image Handling

For the past couple of I have played around with building an iOS app with Firebase as the back-end for it. I wrote a previous post about the joy that is Firebase, which you can check out here. One of the features I am trying to incorporate is taking a picture of an item and uploading it with the post. Well, unfortunately, Firebase does not natively support image uploading. In order to store images with Firebase, you need to first turn them into their Base64 equivalent and store that string value as the value for the “image” key in your Firebase JSON structure.

So, that sounds simple enough until you try to read/write that code in Objective C and JavaScript concurrently. You see, at the same time I am working on the mobile app I want to plan ahead for the web interface for users. Handling Base64 images in either poses its own unique issues and I could not find one Stack Overflow topic that addressed either reasonable well. So, after doing a lot of testing and scouring I want to share the solutions I found for both here.

Objective C / Firebase / Images

Assuming you are capturing an image from the native UIImage tools (camera/film roll), you can convert that image into its Base64 equivalent and then store that as a string value within the JSON object you are writing to Firebase. Before you can do that, however, you will need to include this NSStrinAdditions.h file in your project and declare it in any implementation file that you are using to read/write image files to Firebase.

Import Declaration

#import "NSStrinAdditions.h"

Post Method

UIImage *uploadImage = self.imageView.image;
NSData *imageData = UIImageJPEGRepresentation(uploadImage, 0.9);

// using base64StringFromData method, we are able to convert data to string
NSString *imageString = [NSString base64StringFromData:imageData length:[imageData length]];
    
Firebase* firebaseRef = [[Firebase alloc] initWithUrl:@"https://myFirebase.firebaseio.com/images/data/"];

[addImageRef observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
        long dataLength = snapshot.childrenCount;
        NSString *indexPath = [NSString stringWithFormat: @"%ld", dataLength];
        Firebase* newImageRef = [firebaseRef childByAppendingPath:indexPath];
        [newImageRef setValue:@{@"myImage": imageString, @"someObjectId": @"null"}];

The above takes care of writing an image to a firebase database. The below illustrates how to get that data back and display it accurately in a UIView object. Again, you will need to reference the NSStrinAdditions.h library in your implementation file. In my particular instance I am setting the value of the UIView for my detailViewController that I am transitioning to as part of a segue from a tableViewController.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        detailViewController *destViewController = segue.destinationViewController;
        
        SomeObject* myObject = [listOfObjects objectAtIndex:indexPath.row];
        
        NSData *dataFromBase64=[NSData base64DataFromString:myObject.image];
        UIImage *image = [[UIImage alloc]initWithData:dataFromBase64];
        destViewController.displayImage = image;
        
    } else {
        NSLog(@"ERROR: Unknown segue identifier!");
        exit(1);
    }
}

NOTE: You have to make sure that the destViewController.displayImage = image; that is being set here matches the @property declaration in the destViewController.h file of the destViewController you are using like below.

@property (strong, nonatomic) UIImage *displayImage;

Essentially, that is all it takes to write and read images from firebase using Base64 encoding as your method. To be safe, however, it would be smart to put some image size constraints on the image being used to keep the string value small and the upload/download performance fast.

JavaScript / Firebase / Images

When it comes to processing Base64 image data from Firebase JSON data in web pages I found that Handlebars.js was the best approach. I’ve also written about how easy it is to parse JSON with Handlebars in a previous post. As long as you include the Firebase and Handlebars libraries in your HTML page, see below, you will be able to make just one little change to the src attribute of your img element to display Base64 data as an image.

Script References

	<script src="https://cdn.firebase.com/v0/firebase.js"></script>
	<script src="js/handlebars.js"></script>

Example Image Template for Handlebars

// template for image element
	var imageRef = new Firebase('https://winez.firebaseio.com/images/data');
	var imageSource = '<div id="firebaseImage">' +
					 '<img class="wine" src="data:image/png;base64,{{image}}" />' +
					 '<div>';
	// create a var that gets turned into a handlebars function to parse the returned JSON into the correct expressions in the predefined source template
	var imageTemplate = Handlebars.compile(imageSource);
	// def var for final div to be injected into the page
	var imageResult = "";
	// REST API call for the latest image from Firebase
	var query = imageRef.limit(1);
	query.on('child_added', function(snapshot) {
	  imageResult = imageTemplate(snapshot.val());
	  $('#myImage').append(imageResult);
	});

The key piece in the code above is to make sure to enter this value, data:image/png;base64,{{image}}, into the src attribute of your img element. Anyway, I hope this write-up saves someone, somewhere, some valuable time. I love Firebase a lot but didn’t want to use a separate service for image handling. So, this workaround is a great way of keeping your library/framework inclusions to a minimum. Hopefully, Firebase will update their service to hand images in their default formats in the future.

, , , , , , , ,


4 Responses

  1. Nice post. I am hoping to incorporate the capability with Firebase, but was wondering if it would be easier to handle image processing server side for the upload portion. That way you can include more detailed logging and have once centralized mechanism for the conversion. Certainly the clients to be able to convert data uri, though.

    Have you had any further thoughts on your solution? How has it been running in production?

    • The approach I explained here has worked ok in production but I am thinking of going over to Parse for future development since they handle images better than Firebase does. The plus side of my current approach is that I get the full image every time, the downside is that I get the full image each time (heavy data usage).

  2. […] am trying to upload images into my Firebase database following this article (http://richardbakare.com/thursday-tech-tip-firebase-images/) I’ve already installed Handlebars, however, this is my first time using it and I got stuck […]

  3. Harm Jan says:

    Nice workaround. The only problem I find is that base64 images are usually 25% larger in filesize 🙁 And using a mobile-first strategy with latency risks etc.. that is not optimal.

Leave a Reply