Get Type from Metatype - swift

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.

Related

Nesting structs in Protocol Extension: Type '...' cannot be nested in generic function '...()'

I have a protocol, let's say Fruit (see below).
Within one of the methods I want to use a custom struct.
This results in the following error:
Type 'Packet' cannot be nested in generic function 'saveObject()'
Why is this not allowed?
protocol Fruit: Codable
{
var vitamines: Int { get }
var description: String { get }
}
extension Fruit
{
func saveObject()
{
struct Packet
{
let val1, val2, val3: Int
}
let packet = Packet(val1: vitamines, val2: 0, val3: 0)
}
}
Looking for a solution or viable alternatives.
I've tried using a tuple but I need to save the Packet to Data, which is not easily possible using a tuple (as far as I know).
You cannot nest new types inside of generic structures in extensions this way. Allowing that would likely become very complex, since it is not quite clear whether this type would be Fruit.saveObject.Packet or <ConformingType>.saveObject.Packet. For example, consider the following (legal) code for how these kinds of types can escape, and the system has to deal with them, including knowing how to dispatch methods to them, how much storage they require, etc.
protocol P {}
func x() -> P {
struct T: P {}
return T()
}
type(of: x())
If you change this to make x() generic, then it is no longer legal:
func x<Y>() -> P {
struct T: P {} // error: type 'T' cannot be nested in generic function 'x()'
return T()
}
That said, if you believe that the language should be changed to allow this, then Swift Evolution is the process to suggest it. You should first think though how you would like this to work if Fruit had associatedtypes, if saveObject() were itself generic, and if Packet included reference to type variable defined in either of those places. (I'm not saying these are insurmountable problems at all. This may be an excellent feature and it may be possible to design it very well. You just need to think through how it interacts with other features of the language.)
The solution is to move Packet to the top level, outside the extension and outside the protocol.

implicitely set an associated type

I'm trying to figure out how to implicitely set a Generic (type of a argument) in a class without changing the type of the whole class to something like SomeTestClass< SomeType>, where the owner of an object has to know the type of the generic.
Example
This example DOESNT work! This is how I would like it to work
protocol SomeTestProtocol {
associatedtype T
init(arg: T)
}
Don't want to use SomeTestClass< T>
because the class, which holds this class
wont know the type of the generics used
class SomeTestClass: SomeTestProtocol {
required init(arg: T) {
}
// Use T outside the init-scope
}
Note: The Protocol is just a try for a work-around! It isn't necessary for the final solution
So the main question is: How can I use T outside the init-scope in the class below without using a class-generic, which must be known when owning an object
class SomeTestClass2/*< T>*/ {
init(arg: T) {
}
// Use T outside the init-scope
}
Thanks for your help!
Important note, the T from associatedtype T and the T from init<T> can be different types. They are both defining a generic type with different scopes an could be different. If you want them to be the same, the init should be
init(arg: T)
If SomeTestClass is always going to use the same type, you can add
typealias T = Int // or something
or implement the init as
required init(arg: Int)
It works if you get rid of the associatedtype T from the protocol. Though this removes SomeTestClass.T form existence.
If you declare an associated type in a protocol, that type will become generic over different implementations of the protocol, but each conforming class will need to assign a concrete type to that associate type as a typealias (which can be done implicitly by declaring all variables/functions using the associated type with the same concrete type), so your conforming types won't be generic.
If you want to have generic methods in your conforming classes, you'll need to make the classes themselves generic.
If you only want access to T outside of init, then you just store T in a property:
class S {
let theType: Any.Type
init<T>(arg: T) {
theType = T.self
}
}
I suspect you actually want something different than what you've said you want. I suspect you want to store arg. But if so, what do you plan to do with arg if you have no idea what its type is? What methods can you call? What function could it be the return value of? Without resorting to as?, there's nothing you can do with T (and reliance on as? generally means you've misunderstood your types).
So you need to start with how you want T and SomeTestClass to be used. Without that, it's impossible to discuss how T should be stored (the storage is irrelevant if you never use it).

How to do Type erasure in Swift using if-let?

I see many articles on type-erasure. But most of their examples focus on putting different types into an array.
Is there any way I can get this code to work?
protocol A {
associatedtype Data
func printThis(value: Data)
}
class B {
}
let x = B()
if let y = x as? A { // I get error on this line
// Do nothing
}
Xcode error states
Protocol 'A' can only be used as a generic constraint because it has Self or associated type requirements
This example code is just for demonstration purposes.
As of Swift 4, protocols that have associated type requirements can only be used as generic constraints in function declarations, as in:
func foo<T: A>(t: T) where A.Data: Whatever { ... }
Unless you remove the associated type from the protocol, you cannot just type variables to it; you can only use it to define a generic type.
If Swift ever gains the ability to have generalized existentials in the future, then this may change. But for the time being, this just isn't possible in Swift.

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]

The difference between an any type and a generic type in 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.