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 ³.
- It doesn’t — this stuff is hard to do properly. Anyone who says it isn’t is lying to you.
- Another resolution is to iterate over the
[[String: String]]
returned fromsnapshot.value
, and instantiate eachUser
directly. However, this is more code, is minimally more performant, doesn’t take advantage of the type safety ofCodable
and, if needed, theDecodingError
thrown fromJSONDecoder()
is descriptive, allowing for better debugging, and clearer error handling. - 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!