The difference between an any type and a generic type in swift - swift

What is the difference between an any type and generic type in swift?
Any Type Example:
let swiftInt: Int = 1
let swiftString: String = "miao"
var array: [Any] = []
array.append(swiftInt)
array.append(swiftString)
Generic Type Example:
func duplicate<T>(item: T, numberOfTimes n: Int) -> [T] {
var buffer : [T] = []
for _ in 0 ..< n {
buffer.append(item)
}
return buffer
}
Is this a matter of preference because both appear to solve the same problem by being able to substitute the desired type.

I'm not going to explain generics in details and i'll just point out the essential differences.
In the first example, you'll be able to append any type in that array, without being able to restrict beforehand your array to a specific type and to leverage compile time checks to guarantee that the array will not contain extraneous types. Not much to see in that example.
The second example contains instead a generic function that provides all of the above functionalities, consistency checks on the content of the array will come for free and if you want you'll also be able to specify additional characteristics of that generic type T, like requesting that it implements a specific protocol (e.g. limit duplicate() to object that implement Comparable or Equatable).
But that is just a simple example of a generic function, you can also have parametrized classes (what you'll use the most) and there are a lot of additional functionalities.
Never use Any as a poor-man generics, real generics are way more flexible, add useful checks and make more explicit your intentions, with minimal additional effort required to implement them.

Any means "I don't want any type checking and I won't be able to call type-specific methods without casting"
For example, try to call:
var array: [Any] = [1, 2]
var sum = array[0] + array[1] // you cannot do this! you have to cast to Int first
A generic type is a placeholder for a type. When used, a concrete type is used instead of it (e.g. an Int or a String).
In short, never use Any. There are very very few specific situations when Any is what you want to use.

Related

Compare two objects in Swift without casting to a specific type

I'm trying to compare two objects whose type is known at run time but not at compile time. So far I have the following function which seems to work as written:
/// Compares two objects of the given type.
/// Returns -1 if a is "less than" b, 1 if a is "greater than" b, 0 if they are equal, and nil if no comparison could be made.
func compare<T: Comparable>(type: T.Type, a: Any?, b: Any?) -> Int? {
guard let at = a as? T, let bt = b as? T else { return nil }
return at < bt ? -1 : at > bt ? 1 : 0
}
The problem is, the type is not necessarily known to comply with the Comparable protocol at compile time; I really need to be able to pass in any type (Any.Type), not just Comparable ones. I'd then like the function to return nil if the passed-in type does not conform to Comparable. How can I do this?
Edit (30/08/2018): Some more context. I'm using this function to sort various arrays of strings, integers, and other Comparable types. However, because these arrays are retrieved via reflection the element types are not known at compile time. I know that they will always be Comparable but the compiler doesn't. To resolve this, I'm passing in the type as a separate parameter as shown. However, because I'd like to keep the logic as general as possible I'm performing this sort function inside a conditional statement which chooses the necessary type from an array. The type of this array must be [Any.Type] to hold the required types, even though all its contents conform to Comparable (String.Type, Date.Type, etc.).

Get Type from Metatype

I'm struggling to understand the different between Types and Metatypes in swift 4. In particular I am looking to create an array something like this:
class A { ... }
class B {
func doStuff() {
let otherType = A.self
let itemArr : [otherType] = // Objects of type A
}
}
This throws a compile time error of Use of undeclared type 'otherType' which I think is occurring because otherType is actually A.Type. I think this may have something to do with Generics, but the catch is the type might not be known at compile time...
Is there a solution to this problem?
Swift is powerful because you can pass types as parameters and create variable of types. This is attractive, but yet, there is better solution for your issue.
Define protocol CustomArrayPopulatable {} And add this to all subclasses/classes that could be added to the array. E.G. A1: CustomArrayPopulatable ; A2: CusstomArrayPopulatable.
With generic types, you could achieve great abstraction. However, have in mind, if you need to cast to specific subtype later on, you would need to cast it yourself such as: if type as? A1 {...}, so use generics carefully and think before you start, do you really need them.

What is the type of a Swift Type?

I want to pass an array of types to a function - but I'm not sure what is the type of a Swift Type.
Let's say I have two classes:
MyClass and AnotherClass.
I want an array like this [MyClass, AnotherClass].
What will be the type of the array?
It would be of AnyClass type which is base for all object types.
func checkClassTypes(types: [AnyClass]) {
types.forEach { type in
// Your custom classes don't necessarily have to subclass from NSObject.
print(type is NSObject.Type)
}
}
checkClassTypes([MyClass.self, AnotherClass.self])
If you need to contain only class types, AnyClass will works:
class MyClass {
//...
}
class AnotherClass {
//...
}
let classTypes: [AnyClass] = [MyClass.self, AnotherClass.self]
If you want to include some value types, you may need to use Any or Any.Type:
let types: [Any] = [Int.self, String.self, MyClass.self, AnotherClass.self]
let anyTypes: [Any.Type] = [Int.self, String.self, MyClass.self, AnotherClass.self]
But all three types, AnyClass, Any or Any.Type, are hard to use. You will soon find it is very hard just to instantiate a class in the array.
I'm not sure I understand your question about Swift Types correctly, but Swift doesn't specify a Type as being a certain kind of variable. Swift defines that variables in code can have a certain type, which can be a float, a string, integer, etc. When it is said that Swift is type safe and infers a type, this means that Swift requires you to be clear on the type of values that you store in your variables (type safety) and that Swift will try to infer what type a variable is when you specify a value (type inference).
The type of your variable can also be more complex, like an enum, a struct or a class. Swift even sees an array or other collections as types. In this sense a Type could be defined as a piece of memory in which a constant or a variable (or a collection of these) is stored. A type can be different things. The great thing about having a Type as a constant/variable in which I store information, is that I can now defer the type of a variable until runtime, rather than that I have to specify it in my code. Or I can specify a structure of a generic type and later I can define different kinds of variables with the same structure, but of different types. Please, read for a more detailed understanding of dealing with types the Swift programming language from Apple. It is free and can be downloaded in the iBooks Store.
Now coming back to your second question of an array of [MyClass, AnotherClass] is kind of like deferring the types of your array until later. You can initially define your array as an array of AnyObjects or more swift-like AnyClass, like
myArray = [AnyClass]()
Later you can put different types of variables/objects in that array, like a Double, a String, or an object with a specific class.
You should be careful about building such an array, however. This array is not type safe and you haven't specified the type of object at a certain index. To prevent xcode having trouble with your code and your program from crashing, you should always type cast the individual objects in your array. For instance like
if let myObject = myArray[2] as? myClass { do something }
If you don't typecast your array both xcode and your program will do weird things.
I hope this answered your question.
Kind regards,
MacUserT
I'm guessing that you really want to create an array of instances of the two classes rather than an array of the class definitions. If so, you will be creating an array of Any or AnyObject, not AnyClass.
class MyClass {
}
class OtherClass {
}
let m = MyClass()
let o = OtherClass()
let array1 = [m, o] as [Any]
// or
let array2 = [m, o] as [AnyObject]

Swift generic constraints based on operator

Suppose I want to add up all the values of an entry of an array. Not only integers, but also double values or some type I created myself which implements the + operator. So my question is: Is it possible to create such a function with a constraint that is based on the fact if the type implements the operator? Such as or something like that (obviously THIS isn't working).
Thanks in advance
There are several ways you can approach the problem.
IntegerArithmeticType
While I know of no explicit way to say "Hey Swift, I want to only allow type T where T has this operator", all types we commonly think of as capable of being added, subtracted, multiplied, and divided automatically conform to IntegerArithmeticType, which can be used as a generic constraint for any generic function or type.
For example:
func addAll<T: IntegerArithmeticType>(array: [T]) -> T {
var count = array[0]
for (index, value) in array.enumerate() {
if index != 0 {
count += value
}
}
return count
}
Notice in this quick mock-up version I initialized count with the first value of the array and then avoiding double-counting by checking the index against 0 inside the for loop. I can't initialize count with 0 because 0 is an Int while I want count to be of type T.
You mentioned having your own types work with this too. One option is to have your custom type conform to IntegerArithmeticType, although it requires a lot more than just the + operator. For details on IntegerArithmeticType, see SwiftDoc.
Custom Protocol
If conforming to IntegerArithmeticType imposes some sort of limitation on you, you can create a custom protocol with the + function and whatever other requirements you would like. After all, operators are really just functions. Note that you can add conformance to already existing types with extensions. For example:
protocol Addable {
func +(left: Self, right: Self) -> Self
// Other requirements...
}
extension Int: Addable {}
extension Double: Addable {}

Swift Generics: Constraining Argument Types

Coming from a C++ background (templates), I'm struggling to understand why the following piece of Swift code (generics) does not compile:
func backwards<T>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}
The way I understand it is that T is a generic type on which I do not put any constraint (<T>) and declare array to be of type Array<T>. Yet this produces the following error:
Ambiguous reference to member 'sort()'
I understand that constraints can be put on the generic type using protocols. However, in this case, I don't want any constraint on T. Rather, I want to constrain the type of the first parameter.
I've been reading Apple's documentation on Generic Types for a few hours now but I'm still not much wiser. I have the impression that this is not possible and constraints are put solely on the declared types, but that's as far as I got.
So the question is: If possible, how do I put constraints on the types of the function arguments? If not, how do I achieve the same result?
sort(>) is legal only when Element is Comparable. Since the element type of [T] is T, T must conform to Comparable in order for the array [T] to be sortable via >:
func backwards<T: Comparable>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}