"Ambiguous use of" and constrained generic functions - swift

I have a set of functions that extract values from a dictionary, where the dictionary's value type is Any (it comes from JSON data, and I don't have control over the value types):
func get<T>(_ dict: [String: Any], key: String) throws -> T {
// cast the value for the key to a T, and return
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T {
// cast the value for the key to a T.RawValue, init a T, and return
}
This works fine. The first function can cast a value of a scalar type and return it, while the second can convert a raw value for an enum type into the higher-level Swift type.
If I add a third variant, the compiler becomes unhappy and I get a lot of "ambiguous use of" errors, flagging all three functions as candidates:
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T where T.RawValue == Double {
// try to extract the value as an Double, init a T, and return
// if that fails, extract as an Int, cast to a Double, init a T, and return
}
The idea with this function is that a double value might appear without its decimal, and actually be an Int in the dictionary, but it needs to be a Double in strongly-typed Swift-land.
Why is the compiler able to resolve the ambiguity without the third variant, but considers all three as equally valid candidates when it exists?
Edit: here is a playground that illustrates the problem.
enum E: Error {
case error
}
func get<T>(_ dict: [String: Any], key: String) throws -> T {
guard let value = dict[key] as? T else { throw E.error }
return value
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T {
guard let rawValue = dict[key] as? T.RawValue else { throw E.error }
return T(rawValue: rawValue)!
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T where T.RawValue == Double {
if let doubleValue = dict[key] as? Double {
return T(rawValue: doubleValue)!
} else if let intValue = dict[key] as? Int {
return T(rawValue: Double(intValue))!
}
throw E.error
}
struct Wrapped: RawRepresentable {
typealias RawValue = Double
var rawValue: Double
init?(rawValue: Double) {
self.rawValue = rawValue
}
}
let dict: [String: Any] = ["int": 1, "double": 1.1]
let int: Int = try! get(dict, key: "int")
let double: Double = try! get(dict, key: "double")
let wrapped: Wrapped = try! get(dict, key: "int")
This produces a compile error (ambiguous use of get(_:key:)). Commenting out the third specialization of get<T> resolves the compiler error, but results in a runtime error because the integer value of the key int can't be cast using as? into a Double. Changing the key to double resolves that, but doesn't address the problem I was trying to solve.
I should note that this is now an academic question for me, since I've changed my parsing approach to use Codable and it handles parsing both floating point and integer values into Float/Double types in Swift.

Related

Why my CustomType cannot conform to Decodable even I conformed it?

I have a CustomType that conforms to Decodable, when I want use it as needed value for my function, Xcode complain that CustomType does not conform to Decodable! Should I explicitly make CustomType conformation happen, I also did it, but it did not solved the issue! What I am missing here?
Error:
Type 'CustomType.Type' cannot conform to 'Decodable'
let stringOfJSON: String = """
{ "name": "SwiftPunk", "age": 35 }
"""
let dataOfJSON: Data? = stringOfJSON.data(using: String.Encoding.utf8)
struct CustomType: Decodable {
enum Codingkeys: String, CodingKey {
case name, age
}
var name: String
var age: Int
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Codingkeys.self)
name = try container.decode(String.self, forKey: .name)
age = try container.decode(Int.self, forKey: .age)
}
}
func decoderFunction<T: Decodable>(dataOfJSON: Data?, customType: T, decodedValue: (T) -> Void) {
if let unwrappedDataOfJSON: Data = dataOfJSON {
let dataJSONDecoder: JSONDecoder = JSONDecoder()
do {
let value: T = try dataJSONDecoder.decode(T.self, from: unwrappedDataOfJSON)
decodedValue(value)
} catch {
print("The Data could not be decoded!")
}
}
}
use case:
decoderFunction(dataOfJSON: dataOfJSON, customType: CustomType.self, decodedValue: { value in
})
The type of the customType parameter is incorrect. It should be the metatype of T (T.Type), not T.
func decoderFunction<T: Decodable>(dataOfJSON: Data?, customType: T.Type, decodedValue: (T) -> Void) {
...
}
The method was expecting a value of type T, which should conform to Decodable, but you were giving it a value of type CustomType.Type, which doesn't conform to Decodable (but note that CustomType does).
Also note that you don't need the customType parameter at all. T can be inferred if you specify the type in the closure:
func decoderFunction<T: Decodable>(dataOfJSON: Data?, decodedValue: (T) -> Void) {
...
}
decoderFunction(dataOfJSON: dataOfJSON, decodedValue: { (value: CustomType) in
})
Change the function signature to:
func decoderFunction<T: Decodable>(
dataOfJSON: Data?,
customType: T.Type,
decodedValue: (T) -> Void)
You want to pass in the type of T not a value of T
As you apparently like to annotate types I would omit the type parameter completely and annotate the type in the closure of the caller function.
Further use the Result type to handle a potential error and pass the JSON string to the function rather than optional Data
let stringOfJSON: String = """
{ "name": "SwiftPunk", "age": 35 }
"""
struct CustomType: Decodable {
let name: String
let age: Int
}
func decoderFunction<T: Decodable>(json: String, decodedValue: (Result<T,Error>) -> Void) {
decodedValue(Result {
try JSONDecoder().decode(T.self, from: Data(json.utf8))
})
}
And call it
decoderFunction(json: stringOfJSON) { (result : Result<CustomType,Error>) in
switch result {
case .success(let value): print(value)
case .failure(let error): print(error)
}
}

Cannot convert return expression of type '[(key: String, value: Int)]' to return type '[String : Int]'

I have a custom data struct that looks like this:
struct Entitlements {
var entitlements: [Entitlement]
struct Entitlement {
var type: String
var entitlement: Int
}
}
I have an initialiser that takes a dictionary of [String: Int] and maps it to [Entitlement]:
init(entitlements: [String: Int]) {
self.entitlements = entitlements.map({ (entitlement) -> Entitlement in
return Entitlement(type: entitlement.key, entitlement: entitlement.value)
})
}
I also have a function that maps [Entitlement] to [String: Int]. I have attempted to write it like this:
func simplify() -> [String: Int] {
self.entitlements.map { (entitlement) -> (key: String, value: Int) in
return (key: entitlement.type, value: entitlement.entitlement)
}
}
But I get an error that looks like this:
'Cannot convert return expression of type '[(key: String, value: Int)]' to return type '[String : Int]''
I have tried using flatmap to combine dictionaries into one but with no success:
func simplify() -> [String: Int] {
self.entitlements.flatMap { (entitlement) -> [String: Int] in
return [entitlement.type, entitlement.entitlement]
}
}
But I get an error:
'Unable to infer closure type in the current context'
Any help with this would be much appreciated!
You can use Dictionary(uniqueKeysWithValues:) and map the entitlements array to an array of tuples to get the correct input format for the init.
func simplify() -> [String: Int] {
Dictionary(uniqueKeysWithValues: entitlements.map{ ($0.type, $0.entitlement) })
}
If you need to handle duplicate keys there is also a variant that takes a closure for handling what to do. Here is an example based on Apples doc where the first key is always selected
func toDictionary() -> [String: Int] {
Dictionary(entitlements.map { ($0.type, $0.entitlement) },
uniquingKeysWith: { (first, _) in first })
}

Is there a way to constrain a generic to be one type or another in Swift?

Is there a way to add multiple, optional constraints to a generic? I'd like my generic to be either a string or a bool. Something like:
func myFunction<T>(_ dict: [String: T]) where T == String || T == Bool {
// Do stuff with dict
}
You can just create a protocol and make String and Bool conform to it:
protocol StringOrBool { }
extension Bool: StringOrBool {}
extension String: StringOrBool {}
func myFunction<T: StringOrBool>(_ dict: [String: T]) {
print(dict)
}
myFunction(["a": true])
myFunction(["b": "test"])
myFunction(["b": 1]) // error: MyPlayground Xcode 11.playground:706:1: error: global function 'myFunction' requires that 'Int' conform to 'StringOrBool'
You can't event constrain a generic to one single type:
// error: Same-type requirement makes generic parameter 'T' non-generic
func myFunction<T>(_ dict: [String: T]) where T == String {
}
You probably want some overload of your function for your types:
func myFunction(_ dict: [String: String]) {
print("String")
}
func myFunction(_ dict: [String: Bool]) {
print("Bool")
}
let strings = ["a": "b"]
let bools = ["a": false]
myFunction(strings) // print String
myFunction(bools) // print Bool

Swift Loop through [String: Any] and get difference to Struct or Class properties

I have a struct with several properties. How can I write a function that takes a dictionary of type [String: Any] and creates another dictionary of type [String: Any] that contains only keys and values where the input dictionary's value was different from the struct property value with the same name?
A struct:
struct MyStruct {
var a: Bool = false
var b: String = "random"
var c: Int = 2
}
Desired function call:
let myStruct = MyStruct()
let input: [String: Any] = ["b": "test", "c": 2]
let result: [String: Any] = myStruct.getDiff(input)
Desired result for the example input:
result = ["b": "test"]
Besides a struct, how would this be done for comparing the [String: Any] to a class?
The specific syntax you've provided is probably not possible. Packing things into a [String: Any] likely loses too much information to be recovered. But your overall goal is certainly possible.
The important piece is input. Rather than [String: Any], we're going to use an explicit type, ValueChange:
let input = [
ValueChange(key: "b", changedTo: "test"),
ValueChange(key: "c", changedTo: 2),
]
Creating a new type like this allows us to capture all the types, and enforce certain rules, particularly that the values are Equatable:
struct ValueChange {
init<Value: Equatable>(key: String, changedTo newValue: Value) {...}
}
I'll come back to ValueChange in a moment, but first I want to go back to how it'll be used. Since you want a .getDiff(...) syntax, it'll be best to create an extension using a protocol:
protocol DictionaryDiffComputing {}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
The protocol has no requirements. It just exists to say "these types have the getDiff method." This method needs ValueChange to provide us a (String, Any) tuple if the value has changed.
This is where the problem gets interesting, I'll just show the answer and then discuss it.
struct ValueChange {
let changedValue: (_ object: Any) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, changedTo newValue: Value) {
self.changedValue = { object in
// Get the old value as an Any using Mirror
guard let oldAnyValue: Any = Mirror(reflecting: object)
.children
.first(where: { $0.label == key })?
.value
else {
assertionFailure("Unknown key: \(key)")
return nil
}
// Make sure it's the correct type
guard let oldValue = oldAnyValue as? Value else {
assertionFailure("Bad type for values (\(oldAnyValue)). Expected: \(Value.self)")
return nil
}
// Compare the values
return newValue != oldValue ? (key, newValue) : nil
}
}
}
This uses Mirror to pull out the old value to compare as an Any type, then it converts it to the correct Value type. This is the power of the generic init. Since we know the type a compile time, we can capture it inside this closure, erasing that type from the outside world, but being able to work with it at runtime.
extension MyStruct: DictionaryDiffComputing {}
let myStruct = MyStruct()
myStruct.getDiff(input) // ["b": "test"]
What I really don't like about this answer is that it's very unsafe. Note the two calls to assertionFailure. There is nothing about ValueChange that ensures that the key exists or that the value is the correct type. If you change the name or type a property, your program will either crash or behave incorrectly, and there's nothing the compiler can do to help you.
You can make this a lot more type-safe and the code much simpler at the cost of a slightly more verbose calling syntax:
protocol DictionaryDiffComputing {}
struct ValueChange<Root> {
let changedValue: (_ object: Root) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, keyPath: KeyPath<Root, Value>, changedTo newValue: Value) {
self.changedValue = { newValue != $0[keyPath: keyPath] ? (key, newValue) : nil }
}
}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange<Self>]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
let myStruct = MyStruct()
let input: [ValueChange<MyStruct>] = [
ValueChange(key: "b", keyPath: \.b, changedTo: "test"),
ValueChange(key: "c", keyPath: \.c, changedTo: 2),
]
myStruct.getDiff(input)
If you use this approach, you know that the property exists on this type, and that the value is the correct type for that property. You also get some extra power, since you can use any key path you like starting at this root type. That means you can do things like:
ValueChange(key: "b_length", keyPath: \.b.count, changedTo: 4),
You could cleanup the requirement for key in ValueChange by adding some mapping dictionary of key path to key name (a static var protocol requirement for example), but I don't know of a way to generate this automatically, and I don't know any good way to convert a key path into an appropriate string.

How to pass type (e.g. String) as function argument?

I'm using "JSON to my own class" and I have real headache with type conversions. Typical conversion is looks like:
I need: "55" (JSON) -> 55 (Int)
My path: AnyObject? -> String -> Int
Swift isn't converting AnyObject("55") to Int("55") directly, Swift thinks that "55" is always String. So I've written helper method:
internal func jsonDirectToInt(from: AnyObject?) -> Int? {
guard let string = from as? String,
let int = Int(string) else { return nil }
return int
}
My question: can I pass class type to function as argument, so I can write something like that:
internal func jsonDirectToInt(from: AnyObject?, type: <T>) -> Int? {
guard let string = from as? String,
let int = <T>(string) else { return nil }
return int
}
Possibly I'm misunderstanding what you want to achieve, but it sounds from your question and the third code block that follows that you want a "jsonDirectToAnything" generic function; attempting to convert the JSON from parameter to the type passed via type parameter, using a JSON (AnyObject) -> String -> SomeType pipeline. In your code example above (last code block) your return type is set to Int even when showing that you want some generic <T>(string) conversion (which is assigned to return type), so I will assume that using return type Int is not what you're after, but rather using a generic return type (if not; the question don't really make much sense for me).
Anyway, for the technical discussion: you can create such a function with the generic return type constrained to types that conform to a protocol---say StringInitializable---that includes a blueprint for an (failable) initializer by String instances. You extend the types you want to be able to use the "generic" jsonDirect method to StringInitializable, and, if needed, implement the String initializer blueprinted by the protocol. In the example below, I've blueprinted initializer init?(_ text: String) in StringInitializable. This failable initializer is readily natively available for e.g. Double and String types, but needs to be implemented (just as a wrapper) for e.g. extending Int to the protocol.
Finally note, before we proceed, that there exist several existing tools for handling conversion of JSON data to native Swift types, e.g.
SwiftyJSON
Example solution for your specific question:
/* Protocol with blueprint for failable initializer by String */
protocol StringInitializable {
init?(_ text: String)
}
/* Extend type types you want to use "generically" in 'jsonDirect' method */
extension Double : StringInitializable { } // already has a 'init?(_ text: String)', OK
extension String : StringInitializable { } // already has a 'init?(_ text: String)', OK
extension Int : StringInitializable { // point to 'init(_:radix:)' initializer
init?(_ text: String) {
guard let foo = Int.init(text, radix: 10) else {
return nil
}
self = foo
}
}
/* Your own class */
class MyClass: StringInitializable {
let foo : Int?
required init?(_ text: String) {
foo = Int(text)
}
}
/* jsonDirect for (attempted) type conversion from AnyObject to
generic type, where the latter is constrained to types conforming
to protocol 'StringInitializable' */
func jsonDirect<T: StringInitializable>(from: AnyObject?, toType _ : T.Type) -> T? {
guard let foo = from as? String, let bar = T(foo) else {
return nil
}
return bar
}
Example usage for JSON conversion into Int, Double and String as well as a the custom class MyClass:
/* Example usage */
var myJSONInt : AnyObject = "55"
var myJSONInvalidInt : AnyObject = "foo"
var myJSONDouble : AnyObject = "55.3"
var myJSONString : AnyObject = "Foo"
/* Attempt json -> specified type conversions */
let fooInt = jsonDirect(myJSONInt, toType: Int.self)
let fooMyClass = jsonDirect(myJSONInt, toType: MyClass.self)
let fooInvalidInt = jsonDirect(myJSONInvalidInt, toType: Int.self)
let fooDouble = jsonDirect(myJSONDouble, toType: Double.self)
let fooIntString = jsonDirect(myJSONInt, toType: String.self)
/* Results */
print(fooInt.dynamicType, ": value =", fooInt ?? "nil")
// Optional<Int> : value = 55
print(fooMyClass.dynamicType, ": value =", fooMyClass?.foo ?? "nil")
// Optional<MyClass> : value = 55
print(fooInvalidInt.dynamicType, ": value =", fooInvalidInt ?? "nil")
// Optional<Int> : value = nil
print(fooDouble.dynamicType, ": value =", fooDouble ?? "nil")
// Optional<Double> : value = 55.3
print(fooIntString.dynamicType, ": value =", fooIntString ?? "nil")
// Optional<String> : value = 55