swift use of AnyClass object with as? - swift

I would like to do this:
let myClass: AnyClass = functionReturningClass() // ex. String.self
if let myObject = functionReturningObject() as? myClass { ...
The as? myClass doesn't compile ('myClass is not a type'). What can I do?

Unfortunately this is not possible--as must know the type it is casting to at compile-time. You cannot provide a variable type to as. If you must cast, then you are limited to features like generics and overloading that work at compile-time.
Think about it--what methods should the compiler allow you to call on the resulting myObject? It's impossible to say without knowing the runtime value of myClass. It makes good sense, therefore, that this doesn't work.
You can still return subclasses from functionReturningObject(). You can still cast them to different types. You just can't decide what type to cast them to dynamically at runtime.

Related

What exactly is a metatype in Swift?

I am very confused around the concept of "metatype" in the Swift language.
Suppose I have:
class SomeClass {
class func callClassMethod() {
print("I'm a class method. I belong to my type.")
}
func callInstanceMethod() {
print("I'm an instance method. I belong to my type instance.")
}
}
According to the definition:
A metatype type refers to the type of any type, including class types,
structure types, enumeration types, and protocol types.
SomeClass is already a type called SomeClass, then what exactly is the type of SomeClass?
I can create a SomeClass.Type variable:
let var1 : SomeClass.Type = SomeClass.self
var1.doIt();//"I'm a class method. I belong to my type."
but I can also call the static/class function this way:
SomeClass.doIt();//"I'm a class method. I belong to my type."
Are they the same?
They are the same because the compiler guarantees that class names are unique (Swift is name spaced by module), so there is only one thing that is of SomeClass.Type and that is the class SomeClass. The meta type is often useful when you just want to pass the type of something to a function but you don't want to pass an instance. Codable does this for instance:
let decoded = try decoder.decode(SomeType.self, from: data)
If you could not pass the meta type here then the compiler could still infer the return type based on an annotation on the left, but it would be less readable:
let decoded: Sometype = try decoder.decode(data)
Some libraries do use the type inference style instead, although the Apple preference seems to be to use the meta type as its clearer what the right side of the assignment is on its own, without relying on type inference from the left side of the assignment.

Why is casting a struct to AnyObject not a compile error in swift?

The code below compiles and works in swift.
struct TestStruct {
let value: String = "asdf"
}
func iWantAReferenceType(object: AnyObject) {
print(String(describing: object))
}
let o: TestStruct = TestStruct()
iWantAReferenceType(object: o as AnyObject)
I expected this to be a compile error because a struct can never conform to AnyObject. As demonstrated below by code that fails to compile.
protocol Test: AnyObject {
}
//Compile error: because a struct cannot be AnyObject
struct TestStruct: Test {
let value: String = "asdf"
}
I am aware there is some bridging that can happen for certain types such as String. This would convert the value type of a reference type.
print(Mirror(reflecting: "asdf").subjectType) //print: String
print(Mirror(reflecting: "asdf" as AnyObject).subjectType) //print: NSTaggedPointerString
In writing this question I thought to see what the type of the cast object was and it seems it is also bridged in someway.
print(Mirror(reflecting: o).subjectType) //prints: TestStruct
print(Mirror(reflecting: o as AnyObject).subjectType) //prints: _SwiftValue
Why is this type of casting allowed? It seems to be breaking the contract for the function that is expecting a reference type.
I stumbled on this by accident when refactoring some code to support value types, to my surprise it had already been working for value types even though I thought it wouldn't. Is it safe to rely on this behaviour?
This is a feature to facilitate passing to Cocoa. Any struct can be wrapped into a SwiftValue reference type. If you print type(of: object) you'll see the wrapper.
I don't think there is any contract for "expecting a reference type." More importantly, while "value types" and "reference types" exist in Swift, what really matter is value and reference semantics, which are not expressible in the language. You can create value semantics in reference types and reference semantics in value types, so the Swift type system really isn't of any help in that regard.
The important point here is that you only get this unusual behavior if you explicitly request it by asking for as AnyObject. There are very few reason to write that, and if you are, you had better know exactly what you're doing.

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 - how do I pass a type/object (name or instance) into a method and then (a) get the type (b) instantiate it?

I need to do this, but I'm having all sorts of problems:
class MyClass {
}
class SomeClass {
func callMethod(object) {
let theType = object.type // I need the same result as MyClass.self
let newInstance = object() // I need the same result as MyClass()
}
}
let someClass = SomeClass()
let object = MyClass
someClass.callMethod(object)
How do you actually do this?
I want to be able to specify one type (e.g. MyClass). That can be either the type name or an instance of it and then get the two values shown in the code comments.
It would be a lot easier if I passed in two separate variables, but I'm sure I can do it in one.
You can use the .dynamicType property of an instance to get its type. Then you can call a required init method on it (or any other class/static method).
let theType = object.dynamicType
let newInstance = theType.init()
Note: This code compiles, depending on the type of object. If the type has a required init() method, it is safe to call on the dynamicType. However, it also appears that if the type is NSObject the compiler will let you call init() on the dynamicType even though a subclass may or may not have an init() method given how Swift initializer inheritance works. Arguably this is a bug in the compiler, so use this code with caution if you choose to do so. Thanks to #nhgrif for the discussion on the topic.