Swift: equatable dictionary cannot be passed to a generic function - swift

I really need to pass any equatable structs/types/objects to this setEquatable function.
Any idea how can I solve this problem?
public func ==(l: [String: String], r: [String: String]) -> Bool {
return true // just a stub
}
func setEquatable<T: Equatable>(v: T) {
//...
}
let isEqual = ["1": "2"] == ["1": "2"]
setEquatable(v: ["1": "2"])
For people who vote for closing the question due to duplication: I would not close the question as duplication because that way you will not prevent the same question being asked again in future. People who have problems with their code and want to understand the gist will find this question very useful. They most probably would not know anything about conditional conformance, but they have their code that doesn't compile.

This is currently a limitation of Swift's type system that is well-known and on the roadmap to fix. The specific feature being discussed to fix this is "conditional conformance" to protocols for generic types. In essence, the Dictionary type can't be universally declared Equatable, because you don't know up front how to compare every possible type of value it may hold (some of which might not themselves be Equatable).
Conditional conformance would allow you to create an extension that says that Dictionary sometimes conforms to Equatable, specifically under the condition when its Value type is Equatable. And in that case, Dictionary equality can be defined as a function that compares the equality of all keys and all values in both Dictionary instances being checked.
Here is a summary of this feature and others under consideration:
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#conditional-conformances-
Until this type system upgrade is implemented though, there is unfortunately no way to treat any Dictionary as Equatable directly. You can however create Equatable-conforming wrapper types around Dictionaries, or overload your setEquatable function to also accept Dictionaries with Equatable values and handle accordingly.

Related

A way of declaring the type of a Swift dictionary with many types in it, including embedded dictionaries

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!
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:
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).
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.

In Swift, from a technical standpoint, why does the compiler care if a protocol can only be used as a generic constraint?

In Swift, from a technical standpoint, why does the compiler care if a protocol can only be used as a generic constraint?
Say I have:
protocol Fooable {
associated type Bar: Equatable
func foo(bar: Bar) {
bar==bar
}
}
Why can't I later declare a func that takes a Fooable object as an argument?
It seems to me that the compiler should only care that a given Fooable can be sent a message "foo" with an argument "bar" that is an Equatable and therefore responds to the message, "==".
I understand Swift is statically typed, but why should Swift even really care about type in this context, since the only thing that matters is whether or not a given message can be validly sent to an object?
I am trying to understand the why behind this, since I suspect there must be a good reason.
In your example above, if you wrote a function that takes a Fooable parameter, e.g.
func doSomething(with fooable:Fooable) {
fooable.foo(bar: ???) // what type is allowed to be passed here? It's not _any_ Equatable, it's the associated type Bar, which here would be...???
}
What type could be passed into fooable.foo(bar:)? It can't be any Equatable, it must be the specific associated type Bar.
Ultimately, it boils down the the problem that protocols that reference "Self" or have an associated type end up having different interfaces based on which concrete implementation has conformed (i.e. the specific type for Self, or the specific associated type). So these protocols can be considered as missing information about types and signatures needed to address them directly, but still serve as templates for conforming types and so can be used as generic constraints.
For example, the compiler would accept the function written like this:
func doSomething<T: Fooable>(with fooable:T, bar: T.Bar) {
fooable.foo(bar: bar)
}
In this scenario we aren't trying to address the Fooable protocol as a protocol. Instead, we are accepting any concrete type, T, that is itself constrained to conform to Fooable. But the compiler will know the exact concrete type of T each time you call the function, so it therefore will know the exact associated type Bar, and will know exactly what type may be passed as a parameter to fooable.foo(bar:)
More Details
It may help to think of "generic protocols" — i.e. protocols that have an associated type, including possibly the type Self — as something a little different than normal protocols. Normal protocols define messaging requirements, as you say, and can be used to abstract away a specific implementation and address any conforming type as the protocol itself.
Generic protocols are better understood as part of the generics system in Swift rather than as normal protocols. You can't cast to a generic protocol like Equatable (no if let equatable = something as? Equatable) because as part of the generic system, Equatable must be specialized and understood at compile time. More on this below.
What you do get from generic protocols that is the same as normal protocols is the concept of a contract that conforming types must adhere to. By saying associatedtype Bar: Equatable you are getting a contract that the type Bar will provide a way for you to call `func ==(left:Bar, right: Bar) -> Bool'. It requires conforming types to provide a certain interface.
The difference between generic protocols and normal protocols is that you can cast to and message normal protocols as the protocol type (not a concrete type), but you must always address conformers to generic protocols by their concrete type (just like all generics). This means normal protocols are a runtime feature (for dynamic casting) as well as a compile time feature (for type checking). But generic protocols are only a compile time feature (no dynamic casting).
Why can't we say var a:Equatable? Well, let's dig in a little. Equatable means that one instance of a specific type can be compared for equality with another instance of the same type. I.e. func ==(left:A, right:A) -> Bool. If Equatable were a normal protocol, you would say something more like: func ==(left:Equatable, right:Equatable) -> Bool. But if you think about that, it doesn't make sense. String is Equatable with other Strings, Int is Equatable with other Ints, but that doesn't in any way mean Strings are Equatable with Ints. If the Equatable protocol just required the implementation of func ==(left:Equatable, right:Equatable) -> Bool for your type, how could you possibly write that function to compare your type to every other possible Equatable type now and in the future?
Since that's not possible, Equatable requires only that you implement == for two instances of Self type. So if Foo: Equatable, then you must only define == for two instances of Foo.
Now let's look at the problem with var a:Equatable. This seems to make sense at first, but in fact, it doesn't:
var a: Equatable = "A String"
var b: Equatable = 100
let equal = a == b
Since both a and b are Equatable, we could be able to compare them for equality, right? But in fact, a's equality implementation is limited to comparing a String to a String and b's equality implementation is limited to comparing an Int to an Int. So it's best to think of generic protocols more like other generics to realize that Equatable<String> is not the same protocol as Equatable<Int> even though they are both supposedly just "Equatable".
As for why you can have a dictionary of type [AnyHashable: Any], but not [Hashable: Any], this is becoming more clear. The Hashable protocol inherits from Equatable, so it is a "generic protocol". That means for any Hashable type, there must be a func ==(left: Self, right:Self) -> Bool. Dictionaries use both the hashValue and equality comparisons to store and retrieve keys. But how can a dictionary compare a String key and an Int key for equality, even if they both conform to Hashable / Equatable? It can't. Therefore, you need to wrap your keys in a special "type eraser" called AnyHashable. How type erasers work is too detailed for the scope of this question, but suffice it to say that a type eraser like AnyHashable gets instantiated with some type T: Hashable, and then forward requests for a hashValue to its wrapped type, and implements ==(left:AnyHashable, right: AnyHashable) -> Bool in a way that also uses the wrapped type's equality implementation. I think this gist should give a great illustration of how you can implement an "AnyEquatable" type eraser.
https://gist.github.com/JadenGeller/f0d05a4699ddd477a2c1
Moving onward, because AnyHashable is a single concrete type (not a generic type like the Hashable protocol is), you can use it to define a dictionary. Because every single instance of AnyHashable can wrap a different Hashable type (String, Int, whatever), and can also produce a hashValue and be checked for equality with any other AnyHashable instance, it's exactly what a dictionary needs for its keys.
So, in a sense, type erasers like AnyHashable are a sort of implementation trick that turns a generic protocol into something like a normal protocol. By erasing / throwing away the generic associated type information, but keeping the required methods, you can effectively abstract the specific conformance of Hashable into general type "AnyHashable" that can wrap anything Hashable, but be used in non-generic circumstances.
This may all come together if you review that gist for creating an implementation of "AnyEquatable": https://gist.github.com/JadenGeller/f0d05a4699ddd477a2c1 and then go back an see how you can now turn this impossible / non-compiling code from earlier:
var a: Equatable = "A String"
var b: Equatable = 100
let equal = a == b
Into this conceptually similar, but actually valid code:
var a: AnyEquatable = AnyEquatable("A String")
var b: AnyEquatable = AnyEquatable(100)
let equal = a == b

How to know which type to instantiate from something like JSON?

My app loads a JSON file at launch. The JSON file includes instructions to create various instances of different types. As a basic example, I have a protocol called BasicType with some extensions to make some built-in Swift types conform to it:
protocol BasicType {
}
extension Int: BasicType { }
extension Float: BasicType { }
extension String: BasicType { }
I also have a dictionary which maps these type 'names' (as found in the JSON) to the types themselves:
let basicTypes = [
"integer": Int.self,
"float": Float.self,
"string": String.self
] as [String : BasicType.Type]
When I'm loading BasicTypes from the JSON, I look up the name specified by the JSON using the dictionary above to know which type to instantiate (in reality, the protocol also defines initialisers, so this is possible). I have some other protocols in addition to BasicType which work in exactly the same way, and each one gets its own dictionary map.
For example, my JSON might include an array of BasicTypes I want to instantiate:
{
"basic_types": [{
"type": "integer",
...
}, {
"type": "integer",
...
}, {
"type": "string",
...
}, {
"type": "float",
...
}]
}
The ellipses denote other attributes which are passed to the initialiser, like what value the integer should be. In actuality, these are custom structs and classes which take various properties. In my Swift code, I open up the basic_types array, look at the type key for each JSON object, look up the appropriate types and initialise them. So my loaded array would include two Ints, a String and a Float.
This is similar to how UIKit instantiates views from a storyboard, for example. The view's class name is stored in the storyboard, and in this case presumably it uses something like NSClassFromString to perform the mapping. I don't want to rely on the Objective-C runtime though.
The problem with this approach is that it's difficult to look up the name of a type if I already have the type or instance, as I have to inefficiently iterate the dictionary to search.
Instead, I thought a better solution might be to include each type's name (or 'type identifier') statically as a variable in the type itself, and then generate the type maps from that. So, I created a new protocol:
protocol TypeIdentifiable {
static var typeIdentifier: String { get }
}
And made BasicType (and all the other similar protocols) inherit from it:
protocol BasicType: TypeIdentifiable {
}
Which means I need to provide the names directly in the types themselves:
extension Int: BasicType {
static let typeIdentifier = "integer"
}
extension Float: BasicType {
static let typeIdentifier = "float"
}
extension String: BasicType {
static let typeIdentifier = "string"
}
I made a function (which compiles) which I intended to generically take a protocol conforming to TypeIdentifiable and including an array of types, and produce a dictionary containing the mapping:
func typeMap<T : TypeIdentifiable>(_ types: [T.Type]) -> [String : T.Type] {
return Dictionary(uniqueKeysWithValues: types.map { type in
(key: type.typeIdentifier, value: type)
})
}
I can then replace the dictionary literal with:
let basicTypes = typeMap([
Int.self,
Float.self,
String.self
] as [BasicType.Type])
This way, if I have a type I can easily get its name by accessing its static property, and if I have the name I can get the type through the generated dictionary.
Unfortunately, this doesn't work:
error: cannot convert value of type '[BasicType.Type]' to expected argument type '[_.Type]'
I think using a protocol as a generic type is forbidden in Swift, which probably makes it impossible to do what I'm trying to do.
My question is: Is there a way of doing what I'm trying to do? Otherwise, on a more general level, is there a better way of knowing what types to instantiate from something like JSON?
This is SR-3038 and is roughly "as designed." Protocols do not conform to themselves, so they cannot fulfill generic requirements. BasicType cannot be T because BasicType is a protocol. Why? Well, imagine I've written this code (which I expect is really similar to what you want to write):
protocol P {
init()
}
func makeOne<T:P>(what: T.Type) -> T {
return T.init()
}
Awesome. That's legal. Now, if protocols conformed to themselves, I could write this code:
makeOne(what: P.self)
What init should that call? Swift fixes this impasse by only allowing concrete types to conform to protocols. Since BasicType does not conform to BasicType, it also does not conform to TypeIdentifiable, and cannot be T.
So what do we do?
First and most important-top-of-the-list: Do you actually need this to be generic? How many times do you actually plan to use it for radically different JSON formats? Could you just write the parsing code directly (ideally with the new Codeable)? The tears that have been spilled trying to write complex JSON parsers to parse simple JSON formats could flood WWDC. My rule of thumb is to avoid making things generic unless I already know at least 3 different ways it will be used. (If that happens later, I refactor to generic when I have the examples.) You just don't know enough about the problem until have have several concrete examples and your generic solution will probably be wrong anyway.
OK, let's assume this really is a very general JSON format you have to parse where you have no idea what's going to be in it (maybe like the NIB format you mention). The first and most obvious answer: Use your existing solution. Your concern is that it's hard to look them up backwards (type->identifier), but that's trivial to write a method to make that easy. Unless you have many thousands, possibly millions, of types, the difference in time is beyond trivial; linear search can even be faster than dictionaries for small collections.
Next in line is to just write typeMap for each protocol (i.e. explicitly for BasicType rather than making it generic. There are several cases in stdlib where they do this. Sometimes you just have to do that.
And of course you could create a TypeDefinition struct that holds the info (like a type eraser). In practice this really just moves the problem around. Eventually you'll need to duplicate some code if there are a bunch of protocols.
But almost always when I head down this kind of road, I discover it was way over-engineered and just writing the silly code directly is the easy and scalable solution.
And of course look carefully at the new JSONDecoder in Swift 4. You want to be moving in that direction if you can anyway.

Swift dynamic type checking for structs?

I'm confused about dynamic type checking in Swift.
Specifically, I have a weirdo case where I want to, essentially, write (or find) a function:
func isInstanceOf(obj: Any, type: Any.Type) -> Bool
In Objective-C, this is isKindOfClass, but that won't work because Any.Type includes Swift structs, which are not classes (much less NSObject subclasses).
I can't use Swift is here, because that requires a hardcoded type.
I can't use obj.dynamicType == type because that would ignore subclasses.
The Swift book seems to suggest that this information is lost, and not available for structs at all:
Classes have additional capabilities that structures do not:
...
Type casting enables you to check and interpret the type of a class instance at runtime.
(On the Type Casting chapter, it says "Type casting in Swift is implemented with the is and as operators", so it seems to be a broader definition of "type casting" than in other languages.)
However, it can't be true that is/as don't work with structures, since you can put Strings and Ints into an [Any], and pull them out later, and use is String or is Int to figure out what they were. The Type Casting chapter of the Swift Book does exactly this!
Is there something that's as powerful as isKindOfClass but for any Swift instances? This information still must exist at runtime, right?
Actually you can use is operator.
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
Since struct can't be subclassed, is is guaranteed to be consistent when applied to instance of struct because it will check on it static type, and for classes it will query the dynamic type in runtime.
func `is`<T>(instance: Any, of kind: T.Type) -> Bool{
return instance is T;
}
This work for both, struct and class.
As already stated, is/as should work fine with structs. Other corner cases can usually be done with generic functions, something like:
let thing: Any = "hi" as Any
func item<T: Any>(_ item: Any, isType: T.Type) -> Bool {
return (item as? T) != nil
}
print(item(thing, isType: String.self)) // prints "true"
No idea if this fits your specific use case.
More generally, keep in mind that Swift (currently) requires knowing the type of everything at compile time. If you can't define the specific type that will be passed into a function or set as a variable, neither will the compiler.

any is not identical to anyObject

I've a payload getting shuttled from one part of the system to the other.
The shuttle is carrying the payload as Any, so I could carry any kind of objects including non objects like tuples, etc.
one of the parts of the system is accepting AnyObject so is the error.
I'm confused like what type to use to carry stuff around so it's compatible between all parts of the system.
Shall I make a choice and stick to one of the types, either Any or AnyObject for the system as a whole or what's the best choice for shuttling items if you are not concerned with their actual types.
we had type Object in other languages that could carry anything around, but not sure how this works in SWIFT world
or is there a casting that could work between the two? If I'm 100% convinced that the coming object is AnyObject, I could load it off from the shuttle (Any) as an AnyObject
Note to negative voters: Please help to clear up the question if it doesn't make any sense to you or help to improve this question, since I'm new to SWIFT. I need an answer not your vote.
Edit
a case where I had to do comparison between Any and AnyObject while unit testing, how would you handle such situation.
class Test {
var name: String = "test"
}
var anyObject: AnyObject = Test()
var any: Any = anyObject
//XCTAssert(any == anyObject, "Expecting them to be equal")
any == anyObject
Any will hold any kind of type, including structs and enums as well as classes. AnyObject will only hold classes. So what Any can store is a superset of what AnyObject can. No amount of casting will cram your custom structs or enums into an AnyObject.
Sometimes it seems like AnyObject is holding a struct such as a String, but it isn’t, what has happened is somewhere along the way Swift has converted your String to an NSString (which is a class so can be stored in an AnyObject).
(technically, Any is defined as something that implements 0 or more protocols, which anything does, whereas AnyObject is defined as a special protocol all classes implicitly conform to, and that is marked as an #objc protocol, so only classes can conform to it)
edit: to answer your question about comparisons – there’s no == operator for Any or AnyObject (how would it work if you equated an Any containing a String to an Any containing an Int?). You have to cast both sides back into what they really are before you can compare them using an appropriately-defined operator for that type.