Making a Wedding App with Flip Flop Driven Development
I do a lot of software development by taking the roads less traveled, finding out that they suck, and then having to backtrack. A lot.
This is one of those times.
My wife and I got married just over a week ago, which is a thing that people do, and one of the surprise success stories of the night was an app I had written and published solely to Expo.
The concept behind the app was simple: let our guests take photos like normal, but send the photos to us, as well.
I wanted the app to work on both iOS and Android, across as wide a spectrum as devices as possible, old and new. Having spent a few weeks learning React Native, a Facebook technology that lets you write apps that will work across platforms, I figured it would be a cinch to get something extremely minimal up and running. After all, it was just a camera with an additional upload, right?
As with anything development related, however, you should take estimates with a grain of salt. A cinch quickly turned into two cinches, and then three. All in all, it took probably a day or two of work, including salvaging the photos afterward, to get the app working.
Software has a way of surprising its authors. My journey with this project was particularly circuitous.
T he first thing I did was to set up my project. The technology that you use (React Native, Ruby on Rails, Django, etc.) oftentimes dictates how the project’s files and folders are arranged, with some leeway depending on configuration.
I used create-react-native-app (hereby CRNA) to set up my project because it was the recommended method in Facebook’s documentation . Like command line utilities for many frameworks, isn’t specifically required, but it does help get your project started.
In this case, CRNA shipped with a strange thing called Expo :
I wasn’t sure what Expo did or what it gave me, at first, but as I played around with it, I realized it might help me with a few things:
- Publishing. I didn’t fancy jumping through the hoops to get my app on the Apple App Store or Google’s Play Store. Expo seemed like it might help.
- Cross-platform camera support. Expo’s
Cameracomponent made it easy to write a camera view once, and run it on both iOS and Android.
I was feeling pretty good at this point, so I went hunting around for the next thing — an example of an app that was just a camera, so I could modify it to add a single, simple upload to a service of my choice. I found it in expo/camerja, a repository of source code that did exactly what I wanted — showed a camera preview with a few buttons and nothing else.
Flip flop #1: using create-react-native-app
After eyeballing it for a bit, I realized that I was a bit silly for setting things up as if I were going to start from scratch, so I just cloned expo/camerja and added features as necessary, which turned out to be mostly removing buttons.
(That raises the question —was copying the code allowed? I’ve asked in this issue , and hopefully a response will help clear it up.)
I don’t regret getting comfortable with CRNA and Expo, but it was my first taste of decision-making churn out of many during this project.
I trundled right along, deleting all the bits that I didn’t like.
AF button? I didn’t even know what that was. Zooming with a
+ or a
- button? Bizarre. It was going to be pinch to zoom or nothing. Flash settings? You’re on your own! We’ll let your phone decide on that one. Gallery was a tempting idea, but not really doable in a single night, and it raised uncomfortable questions like, “where am I storing these photos”, “are these photos shared between the guests” and “what will I do if somebody posts something vulgar,” so I applied my favorite code improvement technique to that, too:
This was easy! Or so I thought. As my boss said at the time when I mentioned it, “programming’s really easy when you just copy everything, isn’t it?” :stuck_out_tongue_winking_eye:
The trouble started soon after I started to add the (admittedly conceptually simple) upload functionality.
The idea was that as soon as the user took a photo, it would 1) save the photo to the camera roll, and 2) kick off an upload of that same photo to The Cloud, or whichever cloud I happened to choose. That was it, really.
In order to support uploads, I first had to choose a place to store the photos. Once I had picked a service that would receive the photos, I could figure out how to integrate it into my app.
I had used Firebase Database in the past, and I was eager to try out more Firebase services, trusting the developer experience would be just as excellent. I ended up choosing Firebase Storage. Firebase had never done me wrong before!
Flip flop #2: Firebase did me wrong
…though, to be fair, it was Firebase and Expo. The problem was that Firebase Storage required file uploads to be in a File Blob format. File blobs are exactly what they sound like — a blob of raw data with no particular formatting.
Expo, for all its cross-platform awesomeness, didn’t support the idea of File Blobs. Expo didn’t support it because React Native didn’t support it, and React Native didn’t support it until 2 days after my wedding, which was neither helpful nor timely.
Still easy, though, right? I just had to switch services.
Flip flop #3: This is not so easy
Followed by flip flops 4-8 in quick succession.
I honestly don’t remember exactly what happened for a couple hours as I rifled through various services. I did, however, at least partially implement solutions with the following services:
- Firebase Functions (more on this later)
Simple file hosting seemed to either always require a file blob or just didn’t work for whatever reason. Given that I was working on a week night I kept looking for the easiest, simplest solution, which I kept thinking was just out of reach. After all, it’s just file upload, right?
As frustrating as it was that Expo didn’t support File Blobs, I found that I could configure it to return a Base64 encoded version of the photo — basically if you converted a photo into a string of characters instead of a raw file. This would end up being my saving grace.
Flip flop #4: maybe Firebase is alright, after all!
One of my desperate ploys to get this working was to send this Base64 encoded string somewhere, anywhere to then convert it into a real file. To that end, I toyed around with Firebase Functions, a kind of serverless hosting solution.
Many web applications use a dedicated server as well as a specialized project folder in order to run websites. The conceit behind “serverless” applications is that you don’t need to wire up much of the scaffolding and framework code normally needed to run code on the internet. You just write exactly the code you need and nothing more. It’s the equivalent of using a single wrench instead of a whole toolbox.
My first attempt at this, right around the time I was switching services willy nilly, was a failure. I went to bed that night thinking there must still be something I could do with the Base64 encoded file. In the morning, I happened to search just the right set of keywords and found a solution byWilliam Candillon:
Uploading Images to Firebase with Expo
Like many of you, I’ve tried to upload images to Firebase storage directly from a React Native project attached with… medium.com
Basically it did exactly what I had been hoping to do: took the Base64 encoded file in via a Firebase Function, converted it to a file, and then stored it to Firebase Storage.
Spectacular! I shoved that in my Firebase Function, tested it, and got it working. Then I promptly forgot about it.
Flip flop #5: I don’t care about this app anymore
After a torturous night or two of development on something that should have taken an hour, I decided I was too pooped to try to shepherd the app through to the App Store and Google Play. I had real work to do and I had my hands full with planning and coordinating the wedding itself with my wife.
Still, I already had the app, so I mentioned it in our emails to the wedding guests. The steps to install it went something like this:
- Install Expo on your phone
- Open a web page with a QR code
- Scan the QR code on your phone that you are also using to display the QR code
- Use the app to send us photos!
Of course, it was step 3 that was the head scratcher:
I decided that I just did not care. People would figure it out if they really wanted to use it. I estimated a grand total of 0 people would actually do so. I had place cards to print, a processional to script, and a menu to plan.
Flip flop #6: I care, I really care!
Our wedding went off without a hitch (except for us). Surprisingly, a non-zero number of people used the app to send us photos, despite the fact that the “cool vibration feature” I had kept from the old source code convinced some people that the app was taking blurry photos on purpose.
Still, by the end of the night, we had almost 100 photos. Much better than zero, and a testament to their determination. Not too shabby!
I clicked through the Firebase Storage console and happily looked through our guests’ photos. They would be the first photos we posted to social media.
Flip flop #7: Firebase…again…
I woke up the next morning a completely new, married man, who was still eager to figure out how to get our guest photos off of Firebase Storage. To my horror, I discovered that Firebase Storage wouldn’t let me download my files. When I selected all of them and clicked the
Download button, what happened next depended on what browser I was using:
- Firefox: Firebase downloads a single photo. I thought it was entirely broken. Then I remembered that Firebase is owned by Google, which also owns the internet’s current favorite browser…
- Chrome: The website asks for special permissions to do something called “multi-download” (yes!) and then Firebase downloads exactly 18 photos, in unknown order (no!)
In another series of probably questionable decisions, I decide that it would be easier to script something to grab the 100 files off of Firebase instead of just clicking through and clicking “Download” on batches of 18 photos.
It took me two hours. Because I couldn’t list the contents of the folder I’d uploaded to (thanks Firebase Storage), I had to figure out how to scrape the filenames so that I could get
firebase.js to give me the URLs to download each of the photos, then realized that I would need to change the permissions on the bucket to use
wget to fetch the files...and I’m sure I’m forgetting other issues. In retrospect, it probably would have taken me 15 minutes to just click the photos I wanted to download, and then download them.
Flip flop #8: Do the dumb thing first
I’m still flip flopping on this. Despite having to go through all that trouble, I did learn a lot. As Thomas Edison said:
I have not failed. I’ve just found 10,000 ways that won’t work.
- Thomas Edison
Of course, he had the slight advantage of employing people to try those 10,000 things instead of having to do it himself.
I would have been happy just finishing this whole project faster, but I did manage to learn a thing or two along the way: for instance, the fact that React Native started supporting File Blobs two days after our wedding
Still, there’s an upside to everything. In this case, I got a bunch of friends and family to share their snapshots with us, and learned a lot about developing with React Native, Expo, and Firebase.
If you liked this post, I don’t have anywhere you can sign up to get updates from me. Sorry :stuck_out_tongue:
PS, this post was partially engendered by a discussion that a coworker and I had about how few mid-level blog type resources there were. It seems like everyone either writes about “How To Install Ruby for the First Time!” or they’re writing about how they inverted a binary tree to shave 100ms off of the render time of a custom virtual DOM implementation for their in-game web browser implementation. Hence the tongue-in-cheek tone throughout — I’m pretty confident I’m no longer a beginner, but I am certainly no wizard. If exposing my ignorance helps the development community at large, then so be it.
Also, I like to think that I’m funny.