Firebase DataSnapshot to Swift struct

The requirement was simple — an app to track attendance. It needed to pull down names, display them, and send the selected list to an e-mail address. A straightforward project, one that I could try out some new stuff on.

I had Firebase spitting out data in a couple of hours; clear and easy. I distinctly remember thinking, at this rate, I’d have time to put fancy finishing flourishes into the design.

Inevitably, pride comes before a fall.

Despite uploading data as JSON to Firebase, it is returned as a DataSnapshot, an immutable dump of data wrapped in a custom type.

This is useless for direct conversion to an object using the new Swift 4 Codable interface, with its intrinsic type safety. Also, littering the codebase with references to this object couples everything to the framework.

Nasty business. There had to be a solution.

Of course, the result is always simple, and the time taken to reach it worries you, that it somehow reflects badly on you as a programmer ¹.

Thankfully, snapshot.value provides the data as a native type, in this case, [[String: String]]. And, when this is combined with the custom types adhering to Codable

We are left with Array, Dictionary, String, User and Level — lovely, usable Codable adherents the whole way down.

All that’s left is to take the snapshot.value, cast to its native type, encode to JSON, and decode the JSON to the [User] — Success ²! 🎉 🎊 🍾

But it nags, it niggles, it irritates; is this the best it can be?

🤔 🕐 ☕️ 🕑 ☕️ 🕒 ☕️ 💡

Of course not!

By the power of generics, and extensions, this code can be reused across the app.

Behold, the freshly baked fetch function of DatabaseReference.

Pass this the expected result type, a simple struct that holds the Firebase child path with the snapshot.value type, and 💥 ! Clean, reusable, and helping keep the Firebase references to a minimum ³.

  1. It doesn’t — this stuff is hard to do properly. Anyone who says it isn’t is lying to you.
  2. Another resolution is to iterate over the [[String: String]] returned from snapshot.value, and instantiate each User directly. However, this is more code, is minimally more performant, doesn’t take advantage of the type safety of Codable and, if needed, the DecodingError thrown from JSONDecoder() is descriptive, allowing for better debugging, and clearer error handling.
  3. This very simple app pulls down the whole list of users. In the interests of YAGNI, the extension does not handle more complex use cases. But it could!
Like what you read? Give Jean Power a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.