0

I have a struct which I am initializing, with a dictionary. The values in this dictionary are of many different types: String, Int and Dictionary. This embedded dictionary again, also has many different types (String, Int, Bool, Array and Dictionary). I know, it's a mess. So, I am trying to find the correct syntax for the initializer of this struct which takes as input this big dictionary.

I tried this, but it doesn't satisfy most of my elements.

init(dictionary: [AnyHashable: [AnyHashable: [AnyHashable: Any]]){
...
}

For instance, when trying to access a String on the dictionary's first level, the compiler is expecting a [AnyHashable: [AnyHashable: Any]] type object.

Is there a general way of declaring the type of this dictionary in this initializer? I want to be able to access values and either initialize variables with them or use these values as dictionaries. Is there a way to do that in Swift?

Complementary information: I'm extracting information from my FireStore DB, and this is why I have this heavily layered dictionary. FireStore documents can handle many different levels of information and I had to fill mine up for design purposes.

Thank you!

1 Answers1

0

For instance, when trying to access a String on the dictionary's first level, the compiler is expecting a [String: [String: Any]] type object.

Indeed, because by writing dictionary: [String: [String: [String: Any]], you made a guarantee to the compiler that the values of dictionary are [String: [String: Any]].

If you want your dictionary to be able to hold many different types at runtime, you'll need to give it a static type that is a supertype (protocol or superclass) of all the types you want to use. There's two ways to use such values:

  1. You make the protocol or superclass contain the functions (and properties, subscripts, etc). that you want to use. You can call these functions on the values without needing to know the concrete types of the values. The compiler knows that all values in the dict will support those functions (by virtue of being a subclass of the class, or conforming to the protocol).
  2. Downcast the values (for example, you know that the value for the key "age" will always be an Int) at runtime, into a more specific type that does what you want.

The most general, universally applicable type is Any. But it's generality comes from it not having any requirements, so you really can't do anything with an Any, besides pass it around, ultimately having to downcast it to do anything with it.

TL;DR: You have to use Any, or perhaps some other general protocol type or superclass

Better yet, there's a bunch of libraries for adding Codable functionality to Firebase. I suggest you use one of them.

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Thank you for your answer Alexander. You've really clarified the problem for me and helped me find potential solutions. So i've been spending the last day trying to implement your suggestions. And I've run into issues: So I wan't able to use `Any` as the dictionary value, because I am then using these values as other dictionary keys (and these must be Hashable). So then I implemented a Codable functionality in my class but couldn't convert the firebase `[String: Any]` to a `[String: myCodableStruct]` .... – Antoine Neidecker Apr 23 '20 at 12:52
  • "Any as the dictionary value, because I am then using these values as other dictionary keys (and these must be Hashable)" That's what `AnyHashable` is for. Again, I think you're solving this at the wrong level. The codable system in Swift takes care of 99% of the hardships you're running into by manually unwrapping dictionaries and such – Alexander Apr 23 '20 at 12:59
  • So I looked into libraries such as [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper) and [CodableFirebase](https://github.com/alickbass/CodableFirebase) but ran into the same issue for both. Which was [this](https://stackoverflow.com/questions/32605504/xcode-duplicate-symbols-for-architecture-error-after-updating-cocoa-pods). And went down that rabbit whole unable to solve it – Antoine Neidecker Apr 23 '20 at 13:00
  • @AntoineNeidecker I think I've run into that error before, but IDR how I solved it. I think that's the better problem to focus on, it'll leave your code base in a better space – Alexander Apr 23 '20 at 13:03
  • And you didn't run into [this issue](https://stackoverflow.com/questions/61384678/swift-preprocessing-error-expected-end-of-line-in-preprocessor-expression/61385942#61385942) when you were solving it by any chance? – Antoine Neidecker Apr 23 '20 at 13:07
  • @AntoineNeidecker Doesn't ring a bell, no. Though I've never used Firebase – Alexander Apr 23 '20 at 13:09