How Are Int and String Accepted as Anyhashable

How are Int and String accepted as AnyHashable?

Consider that Optional is an enum, which is also a value type – and yet you're freely able to convert a String to an Optional<String>. The answer is simply that the compiler implicitly performs these conversions for you.

If we look at the SIL emitted for the following code:

let i: AnyHashable = 5

We can see that the compiler inserts a call to _swift_convertToAnyHashable:

  // allocate memory to store i, and get the address.
alloc_global @main.i : Swift.AnyHashable, loc "main.swift":9:5, scope 1 // id: %2
%3 = global_addr @main.i : Swift.AnyHashable : $*AnyHashable, loc "main.swift":9:5, scope 1 // user: %9

// allocate temporary storage for the Int, and intialise it to 5.
%4 = alloc_stack $Int, loc "main.swift":9:22, scope 1 // users: %7, %10, %9
%5 = integer_literal $Builtin.Int64, 5, loc "main.swift":9:22, scope 1 // user: %6
%6 = struct $Int (%5 : $Builtin.Int64), loc "main.swift":9:22, scope 1 // user: %7
store %6 to %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %7

// call _swift_convertToAnyHashable, passing in the address of i to store the result, and the address of the temporary storage for the Int.
// function_ref _swift_convertToAnyHashable
%8 = function_ref @_swift_convertToAnyHashable : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1 // user: %9
%9 = apply %8<Int>(%3, %4) : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1

// deallocate temporary storage.
dealloc_stack %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %10

Looking in AnyHashable.swift, we can see the function with the silgen name of _swift_convertToAnyHashable, which simply invokes AnyHashable's initialiser.

@_silgen_name("_swift_convertToAnyHashable")
public // COMPILER_INTRINSIC
func _convertToAnyHashable<H : Hashable>(_ value: H) -> AnyHashable {
return AnyHashable(value)
}

Therefore the above code is just equivalent to:

let i = AnyHashable(5)

Although it's curious that the standard library also implements an extension for Dictionary (which @OOPer shows), allowing for a dictionary with a Key of type AnyHashable to be subscripted by any _Hashable conforming type (I don't believe there are any types that conform to _Hashable, but not Hashable).

The subscript itself should work fine without a special overload for _Hashable keys. Instead the default subscript (which would take an AnyHashable key) could just be used with the above implicit conversion, as the following example shows:

struct Foo {
subscript(hashable: AnyHashable) -> Any {
return hashable.base
}
}

let f = Foo()
print(f["yo"]) // yo

Edit: In Swift 4, both the aforementioned subscript overload and _Hashable have been removed from the stdlib by this commit with the description:

We have an implicit conversion to AnyHashable, so there's no
need to have the special subscript on Dictionary at all.

Which confirms my suspicion.

Convert userInfo [AnyHashable: Any] to [String: Any]

For the following answer, the code is not tested against a compiler, there might be some typo issue that could be easily fixed, some of them are intentionally done to exergue the logic behind it, and not add with if let/guard let, as?, etc. that are needed but add noise in the explanation.

I won't repeat @vadian answer, which is correct an explain why it fails.

So we are clear that dict["message"] is a String.

A piece of information that you seem to be missing in the JSON acronym is for what stands the "N": Notation.

When you printed dict["message"], you didn't have really a key/value object, you have a String representing a key-value object, but not in a Swift representation. You printed JSON Stringified (because it's clearly more readable that hex data JSON). If after the answer you print jsonDict, you'll see that the output structure might be different.

So, as always, your basic tools are:

Data <== data(encoding:)/init(data:encoding:) ==> String
Data <== jsonObject(with:options:)/data(withJSONObject:options:) ==> Array or Dictionary //I bypass voluntarily the specific case of String at top level

Let's do it then!

let jsonStringifiedString = dict["message"] as String
let jsonStringifiedData = jsonStringifiedString.data(using: .utf8) as Data
let jsonDict = try JSONSerialization.jsonObject(with: jsonStringifiedData, options: []) as [String: Any]
let baseResponse = Mapper<NotificationModel>().map(JSON: jsonDict)

If I were you, I'd look into Mapper if there is no way to do something like:

let baseResponse = Mapper<NotificationModel>().map(JSONData: jsonStringifiedData)

or

let baseResponse = Mapper<NotificationModel>().map(JSONString: jsonStringifiedString)

Because there are sometimes JSONStringified embedded in JSON, where you might need to call it on a String or on a Data directly.
Or just because the basic URLSession request returns a Data object in its closure, and you want to use it directly.

Looping an AnyHashable Dictionary

You can try

var element = dictionaryMessage!["precios"] as! [[String:Any]]

Binary operator '==' cannot be applied to operands of type '[AnyHashable : Any]?' and 'String'

As the error clearly states info is a dictionary. You have to check if the dictionary contains the error key

if let error = info[PHImageErrorKey] as? String {
print(error)
// show an alert
} else {
thumbnail = result!
}

‘AnyHashable’ is not convertible to ‘NSObject’

Maybe you need to change your array type

var myMutableArray = [AnyHashable]()

to

var myMutableArray = [DCclass]()

And then in the for loop you can access it like this

for dc in myMutableArray {    
print("DCclass.empName = \(dc.empName)")
}


Related Topics



Leave a reply



Submit