Calling a generic function in swift - swift

I have the following struct, variable and a function:
struct MyModel {
var keyString: String
var keyNum: Int
}
let data = "{\"keyString\": \"valueString\", \"keyNum\": 1 }"
func myFunction<T: AnyObject>(str: String) throws -> T? {
return nil
}
How can I call the function with MyModel? Below code makes the compiler complain: "Generic parameter 'T' could not be inferred"
let myModel = try? myFunction(str: data) as? MyModel
Link to Swift REPL: http://swiftlang.ng.bluemix.net/#/repl/57f1fa479ce3c95fc38e63b3

Since your generic type parameter is declared with a constraint, like this:
func myFunction<T: AnyObject>
So whatever type you use, it must be a reference type or implement NSObjectProtocol or something like that.
A swift struct is a value type, so MyModel can't possibly be used as a generic type parameter.
Solutions:
Change the generic type constraint to <T: Any> to make it accept value types.
Remove the generic type constraint.
Change the struct to a class and add an initializer.

Related

Check if object is of generic type with base generic type

With the following architecture:
protocol ProtocolA;
struct MyStruct : ProtocolA;
struct OtherStruct<some ProtocolA>;
let obj = OtherStruct<MyStruct>()
I want something like this
if obj is OtherStruct<any ProtocolA> {
/// This dont work
}
Any idea?
You can't declare a generic type using some since it should be a protocol the type has conform to and not the type itself. So the correct declaration of OtherStruct is
struct OtherStruct<Object: ProtocolA> {
// ...
}
Then if want to check if an object is of type OtherStruct then this is implicitly done for instance in a generic function
func doSomething<T: ProtocolA>(with object: OtherStruct<T>) {
// do stuff
}
since doSomething can only be called with a generic type where the type parameter conforms to ProtocolA
doSomething(with: OtherStruct<MyStruct>()) // works
but not something else
doSomething(with: OtherStruct<String>()) // compilation error
And the same logic applies if we use OtherStruct as a property type in another type
struct Example<Object: ProtocolA> {
var other: OtherStruct<Object>
}

Generic function and protocol Swift 4.2 +

I am having an issue creating a function that returns a generic type that conforms to the Codable protocol.
To give more context:
Below is a protocol with an associated type:
public protocol CodableProtocol {
associatedtype T: Codable
var httpMethod: HTTPMethods { get }
func toDictionary() -> T
}
Notice the toDictionary() method that returns a generic type that must conform to codable. For the most part it is easy to implement toDictionary() by defining the return as Dictionary<String, String>
However, the problem arises when the Dictionary has more than one data type.
For example: [String: [String: String/ Int], String: String]
The value for the key in the dictionary is codable, so basically when implementing toDictionary() one needs to return a generic type that is constrained to Codable.
My problem is implementing the toDictionary() in the class returning a generic type and having that class conform to the protocol. I almost feel that defining a type alias in the class could fix it, but when I do it breaks conformance to the protocol.

what is T.Type in swift

Can anyone tell me what is T.Type when I use JSONDecoder().decode()?
I think it is type to decode data what I encoded.
So many example use above method like this:
JSONEncoder().decode([People].self, from: data)
If I check that method's definition I can see T.Type.
I know the generics but what is T.Type?
What's the difference between just T and T.Type?
when we declare some variables, we allocated their types like this
var someValue: Int
, not var someValue: Int.self
What is the T.Type exactly and Type.self?
T.Type is used in parameters and constraints to mean "the type of the thing itself, not an instance of the thing".
For example:
class Example {
static var staticVar: String { return "Foo" }
var instanceVar: String { return "Bar" }
}
func printVar(from example: Example) {
print(example.instanceVar) // "Bar"
print(example.staticVar) // Doesn't compile, _Instances_ of Example don't have the property "staticVar"
}
func printVar(from example: Example.Type) {
print(example.instanceVar) // Doesn't compile, the _Type_ Example doesn't have the property "instanceVar"
print(example.staticVar) // prints "Foo"
}
You get a reference to a Type's .Type (the Type object itself) at runtime by calling TheType.self. The syntax TheType.Type is used in type declarations and type signatures only to indicate to the compiler the instance vs. type distinction. You can't actually get a reference to, for example, Int's type at runtime or in your function implementations by calling Int.Type. You would call Int.self
In the example code var someValue: Int, the specific notation identifier: Type (in this case, someValue: Int) means that someValue will be an instance of Int. If you wanted someValue to be a reference to the actual type Int, you would write var someValue: Int.Type = Int.self Remember that the .Type notation is only used when declaring types and type signatures to the compiler, and the .self property is used in actual code to retrieve a reference to the type object itself at execution time.
The reason why JSONDecoder().decode requires a parameter of T.Type (where T conforms to Decodable) is because any type conforming to Decodable has an initializer init(from decoder: Decoder). The decode method will need to call this init method on a type that conforms to Decodable, not on an instance of a type that conforms to Decodable. For example:
var someString: String = ""
someString.init(describing: 5) // Not possible, doesn't compile. Can't call an initializer on an _instance_ of String
var someStringType: String.Type = String.self
someStringType.init(describing: 5) // Iniitializes a String instance "5"

How do you cast in swift using a variable for the type

I'm looking to try and cast a type based on a variable. The example below fails saying "Use of undeclared type, 'myType'"
protocol Parent {}
protocol Child: Parent {}
struct Foo: Child {}
let foo: Parent = Foo()
let myType = Child.self
if let _ = foo as? myType {
print("success")
}
Is there any way to cast based on the variable myType?
Having reflected, casting is not the right question to ask here. Here i'm just checking for conformance. Dynamic casting would be useless as there's no way for the static analyzer to know what to do with it if it's casting based on a variable type.

How to create Dictionary that can hold anything in Key? or all the possible type it capable to hold

I want to create a Dictionary that does not limit the key type (like NSDictionary)
So I tried
var dict = Dictionary<Any, Int>()
and
var dict = Dictionary<AnyObject, Int>()
resulting
error: type 'Any' does not conform to protocol 'Hashable'
var dict = Dictionary<Any, Int>()
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Any, Int>()
^~~~~~~~~~~~~~~~~~~~~~
OK, I will use Hashable
var dict = Dictionary<Hashable, Int>()
but
error: type 'Hashable' does not conform to protocol 'Equatable'
var dict = Dictionary<Hashable, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Hashable, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~~
So Hashable inherited from Equatable but it does not conform to Equatable??? I don't understand...
Anyway, keep trying
typealias KeyType = protocol<Hashable, Equatable> // KeyType is both Hashable and Equatable
var dict = Dictionary<KeyType, Int>() // now you happy?
with no luck
error: type 'KeyType' does not conform to protocol 'Equatable'
var dict = Dictionary<KeyType, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'KeyType' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:6:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<KeyType, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~
I am so lost now, how can I make compiler happy with my code?
I want to use the dictionary like
var dict = Dictionary<Any, Int>()
dict[1] = 2
dict["key"] = 3
dict[SomeEnum.SomeValue] = 4
I know I can use Dictionary<NSObject, Int>, but it is not really what I want.
Swift 3 update
You can now use AnyHashable which is a type-erased hashable value, created exactly for scenarios like this:
var dict = Dictionary<AnyHashable, Int>()
I believe that, as of Swift 1.2, you can use an ObjectIdentifier struct for this. It implements Hashable (and hence Equatable) as well as Comparable. You can use it to wrap any class instance. I'm guessing the implementation uses the wrapped object's underlying address for the hashValue, as well as within the == operator.
I took the liberty of cross-posting / linking to this question on a separate post on the Apple Dev forums and this question is answered here.
Edit
This answer from the above link works in 6.1 and greater:
struct AnyKey: Hashable {
private let underlying: Any
private let hashValueFunc: () -> Int
private let equalityFunc: (Any) -> Bool
init<T: Hashable>(_ key: T) {
underlying = key
// Capture the key's hashability and equatability using closures.
// The Key shares the hash of the underlying value.
hashValueFunc = { key.hashValue }
// The Key is equal to a Key of the same underlying type,
// whose underlying value is "==" to ours.
equalityFunc = {
if let other = $0 as? T {
return key == other
}
return false
}
}
var hashValue: Int { return hashValueFunc() }
}
func ==(x: AnyKey, y: AnyKey) -> Bool {
return x.equalityFunc(y.underlying)
}
Dictionary is struct Dictionary<Key : Hashable, Value>...
Which means that Value could be anything you want, and Key could be any type you want, but Key must conform to Hashable protocol.
You can't create Dictionary<Any, Int>() or Dictionary<AnyObject, Int>(), because Any and AnyObject can't guarantee that such a Key conforms Hashable
You can't create Dictionary<Hashable, Int>(), because Hashable is not a type it is just protocol which is describing needed type.
So Hashable inherited from Equatable but it does not conform to
Equatable??? I don't understand...
But you are wrong in terminology. Original error is
type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
That means that Xcode assuming 'Hashable' as some type, but there is no such type. And Xcode treat it as some kind empty type, which obviously does not conform any protocol at all (in this case it does not conform to inherited protocol Equatable)
Something similar happens with KeyType.
A type alias declaration introduces a named alias of an existing type into your program.
You see existing type. protocol<Hashable, Equatable> is not a type it is protocol so Xcode again tells you that type 'KeyType' does not conform to protocol 'Equatable'
You can use Dictionary<NSObject, Int> just, because NSObject conforms Hashable protocol.
Swift is strong typing language and you can't do some things like creating Dictionary that can hold anything in Key. Actually dictionary already supports any can hold anything in Key, which conforms Hashable. But since you should specify particular class you can't do this for native Swift classes, because there is no such master class in Swift like in Objective-C, which conforms air could conform (with a help of extensions) to Hashable
Of course you can use some wrapper like chrisco suggested. But I really can't imagine why you need it. It is great that you have strong typing in Swift so you don't need to worry about types casting as you did in Objective-C
Hashable is just a protocol so you can't specify it directly as a type for the Key value. What you really need is a way of expressing "any type T, such that T implements Hashable. This is handled by type constraints in Swift:
func makeDict<T: Hashable>(arr: T[]) {
let x = Dictionary<T, Int>()
}
This code compiles.
AFAIK, you can only use type constraints on generic functions and classes.
This doesn't exactly answer the question, but has helped me.
The general answer would be implement Hashable for all your types, however that can be hard for Protocols because Hashable extends Equatable and Equatable uses Self which imposes severe limitations on what a protocol can be used for.
Instead implement Printable and then do:
var dict = [String: Int]
dict[key.description] = 3
The implementation of description has to be something like:
var description : String {
return "<TypeName>[\(<Field1>), \(<Field2>), ...]"
}
Not a perfect answer, but the best I have so far :(
This does not answer the OP's question, but is somewhat related, and may hopefully be of use for some situations. Suppose that what you really want to do is this:
public var classTypeToClassNumber = [Any.Type : Int]()
But Swift is telling you "Type 'Any.Type' does not conform to protocol Hashable".
Most of the above answers are about using object instances as a dictionary key, not using the type of the object. (Which is fair enough, that's what the OP was asking about.) It was the answer by Howard Lovatt that led me to a usable solution.
public class ClassNumberVsClassType {
public var classTypeToClassNumber = [String : Int]()
public init() {
classTypeToClassNumber[String(describing: ClassWithStringKey.self)] = 367622
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList3.self)] = 367629
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList2.self)] = 367626
classTypeToClassNumber[String(describing: ClassWithGuidKey.self)] = 367623
classTypeToClassNumber[String(describing: SimpleStruct.self)] = 367619
classTypeToClassNumber[String(describing: TestData.self)] = 367627
classTypeToClassNumber[String(describing: ETestEnum.self)] = 367617
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList0.self)] = 367624
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList1.self)] = 367625
classTypeToClassNumber[String(describing: SimpleClass.self)] = 367620
classTypeToClassNumber[String(describing: DerivedClass.self)] = 367621
}
public func findClassNumber(_ theType : Any.Type) -> Int {
var s = String(describing: theType)
if s.hasSuffix(".Type") {
s = s.substring(to: s.index(s.endIndex, offsetBy: -5)) // Remove ".Type"
}
let classNumber = _classTypeToClassNumber[s]
return classNumber != nil ? classNumber! : -1
}
}
EDIT:
If the classes involved are defined in different modules, and may have conflicting class names if you neglect the module name, then substitute "String(reflecting:" for "String(describing:", both when building up the dictionary and when doing the lookup.
You can use the class name as a Hashable, e.g.:
var dict = [String: Int]
dict[object_getClassName("key")] = 3
See How do I print the type or class of a variable in Swift? for how you might get the class name.