I have faced an issue. Consider I have one protocol and two classes implementing it:
protocol Initiatable{
init()
}
class A: Initiatable{
required init() {}
}
class B: Initiatable{
required init() {}
}
then at some point I'm creating an array and passing it to function:
var array = [A]()
func update(object: Any){
}
update(object: array)
from that function update
I would like to pass object
to another function if it mets another function's conditions:
func process<T: Initiatable>(array: T){
/* ... */
}
So how can I check that object of type Any
is an array of concrete class implementing Initiatable
protocol? I would like to write something like
func update(object: Any){
if let array = object as Array<T: Initiatable>{
process(array: array)
}
}
But that's doesn't work. Code like:
func update(object: Any){
if let array = object as [Initiatable]{
process(array: array)
}
}
func process(array: [Initiatable]){ }
Compiles fine, but that's not what I want – process
function should receive an array of concrete implementation of Initiatable
so at some point it could use:
func process<T: Initiatable>(array: [T]){
/* other code */
T.init()
}
So is there any way to do this? Thank you very much in advance!
One of which is Array of Objects, in TypeScript, the user can define an array of objects by placing brackets after the interface. It can be named interface or an inline interface.
We defined an interface for an array of objects; we explained the interface for the type of each object. Put the array to be Type []. All the objects added to the array must obey the type, or the type checker gives an error. In the above example, the type of an array of objects is used as an interface.
In javascript, there is no type checking, declared variable holds any type of data includes array or any type. We need to know the object type based on the data stored.
Object constructor name return type of the object like an object,Number,String,Array. the following checks constructor name is array or not Array inbuilt-in method isArray checks for objects, This is part of the native method in javascript language, installation of libraries is not required.
There's a few parts to this question:
Generating the array of types
Your array declaration is expecting an array of A
objects rather than A
types. To generate an array with A types, you could pass in a Postfix self
expression: (link)
var array = [ A.self ]
This would define array
as an array of A.Type
, called the Metatype Type (same link).
You could also generate an empty array with this metatype type:
var array:[A.Type] = []
If you wanted an array with both A.self
and B.self
you could either specify it as [Any]
...
var array:[Any] = [A.self,B.self]
...or, make use of the Initiatable
protocol you created:
var array:[Initiatable.Type] = [A.self,B.self]
Downcasting an array to an array of types in your update method
You were having trouble downcasting an Any object to an array of types.
Here's my updated update
method:
func update(object: Any){
if let array = object as? [Initiatable.Type] { //1
process(array: array)
}
}
update
method. Downcast it to an array of metadata types of Initiatable
: (This is the only line I modified from your method)Receiving a type as a parameter in the process method
I'm assuming that you just want your process method to receive an array of types, and instantiate a variable based on one of those types. You didn't mention which element in the array, so I've just gone with the first.
func process(array: [Initiatable.Type]){ //1
if let firstType = array.first { //2
let firstObject = firstType.init() //3
}
}
process
method can receive an array of types that adopt the Initiatable protocol.If your array is nonempty, then you can solve this by grabbing the runtime type of one element from the array:
func update(object: Any){
if let array = object as? [Initiatable]{
process(array: array)
} else {
// Not initiatable
}
}
func process(array: [Initiatable]) { // no need for generics here
guard let first = array.first else {
// Unable to determine array type
// throw or return or whatever
}
// type(of: first) gives A or B
type(of: first).init()
}
If the array is empty, then I don’t know of a way to do this in Swift (as of Swift 3, anyway).
object
is Any
, so you need to use the runtime type of object
in order to get anywhere.T
based on the static types of the values you pass. Therefore your whole approach with process<T: Initiatable>(array: [T])
is a dead end: T
can only take on a type the compiler already knew when you called process()
.type(of: object)
will return (for example) Array<A>
. So close! But:
A
from Array<A>
, unless the compiler already knows the type parameter statically."Array<A>"
and extract the string A
from it, but AFAIK there is no way to map strings back to types in Swift unless the type is an Objective-C class.In short, I think you’ve just hit a limit of Swift’s metatype / reflection capabilities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With