Array of types of types

In Rust, there is something called a TypeId which is universally unique identifier for a type, is there an analogue of this in swift?
What I want to do is have an array of the types of types. I read about the metatype, but metatype isn't actually type. I tried something like:

struct SomeStruct{}
struct SomeStruct2{}
let ar=[SomeStruct.Type, SomeStruct2.Type]

But Xcode complains about this being a heterogeneous array and resolves the type as [Any]. Is there a more idiomatic way to do this in swift? Or should I just use Any? I'm extremely new to the language (started learning it yesterday), so forgiveness if this sounds dumb.

1 Like

Your array should be [SomeStruct.self, SomeStruct2.self] instead of using .Type. That’s because SomeStruct.self is an instance of the metatype SomeStruct.Type, and arrays need to hold actual values not types.

But as you mention, you can’t do that because arrays are homogeneous and you want to store instances of different (meta)types. What you’re looking for is probably ObjectIdentifier, with which you can write:

let ar: [ObjectIdentifier] = [ObjectIdentifier(SomeStruct.self), ObjectIdentifier(SomeStruct2.self)]

Hope this helps! Good luck with learning Swift and good job for reaching this level at day 2.

2 Likes

Thank you! I'll try that.

Is it possible to create a generic function that would be able to do this, like so:

func oi<T,U>()->[ObjectIdentifier]{
return [ObjectIdentifier(T.self),ObjectIdentifier(U.self)]
}

Swift complains that the generic parameter isn't used in function signature

It is possible to hold onto an array of types, but they must be explicitly erased to Any.Type:

struct SomeStruct {}
struct SomeStruct2 {}
let ar: [Any.Type] = [SomeStruct.Type, SomeStruct2.Type]

From here you could map them all to ObjectIdentifier if you need a unique/hashable type:

let ids = ar.map(ObjectIdentifier.init)
1 Like

@stephencelis’s solution is the most appropriate for your use case.

But in general Swift expects that generic parameters in functions actually appear as parameters or are used in constraints. Thus, your function would become:

func oi<T, U>(_: T.Type, _: U.Type) -> [ObjectIdentifier] {
    return [ObjectIdentifier(T.self), ObjectIdentifier(U.self)]
}

Otherwise, Swift wouldn’t be able to infer what T and U are. I don’t know if it’s possible in Rust, but in Swift you can’t do oi<Int, String>(), but can only specify the parameters: oi(Int.self, String.self).

Oh, okay. And yes, marker-only generics are reasonably common in Rust

Also, in general, does swift recommend using composition over inheritance? There seems to be some overlap between the possible usage of protocols and class inheritance. So, if I wanted to make UI components, that each have a different way of rendering themselves, do I create a class Component and subclasses, or a protocol ComponentPro (or something)? In Rust (which has no 'classes' or inheritance), there's only one option, and in the more classical OOP languages, the opposite is generally preferred. In my cursory reading so far, there seem to be a lot of Protocols in the standard libraries (Foundation and SwiftUI)?

Short answer: yes, you will find composition with protocols, often with value types (structs, enums) quite prevalent in idiomatic Swift.

Often this boils down to the Framework you are using, though. E.g., when it comes to Apple's user interfaces, both UIKit and AppKit pre-date Swift and very heavily utilise inheritance (e.g. through subclasses of UIView/NSView, or UIControl/NSControl). Compare this with SwiftUI, which heavily uses structs+protocols (e.g. for views) and functions (e.g. for view modifiers).

While Foundation technically also predates Swift, the Swift version of Foundation heavily utilises value types (and the Apple version bridges most of the Objective-C classes and class clusters into Swift value types).

So, if I wanted design the above mentioned component structure, I ought to create a protocol instead of a class, to be idiomatic (this will be used with SwiftUI)?

Describe what you actually want to do and we may be able to suggest a design. So far it's unclear what you're actually trying to do.

So, I have some class/struct (I'm not sure which is best in this case, but let's say struct) called Entity, like so:

struct Entity{
let id:UUID
components:*Something*
}

An entity contains a variable amount of components, each of which contain different kinds of data (and renders itself differently). I'm writing this to interact with some rust code (over TCP/IP) which has a proper ECS system and contains all the actual functionality, and I need to develop a UI frontend for this system, that just gets the data, and renders it.

I'm thinking of separating this into models and views, like I've seen in the tutorials. So I'd make a component protocol:

protocol Component{
let component_id:UUID
*other stuff*...
}

And then create structs that conform to that protocol representing the actual components.

MyComponent:Component{
...
{

And an entity view like so:

struct EntityView:View{
var entity_model:Entity

}
I then need a way to loop through all the components and ask them to render themselves in their own way. Should the Component protocol itself be a view? Or something else. Thank all of you again for the help so far.
With SwiftUI it would be something like this,
import SwiftUI

enum Item: Hashable, Identifiable {
    case item1(Item1)
    case item2(Item2)
    
    var id: String {
        switch self {
        case .item1(let v): return v.id
        case .item2(let v): return v.id
        }
    }
}

struct Item1: Hashable, Identifiable {
    var id: String
    var name: String
    // ...
}

struct Item2: Hashable {
    var id: String
    var imageName: String
    // ...
}

// MARK: views

struct Item1View: View {
    let item: Item1
    var body: some View {
        Text(item.name)
    }
}

struct Item2View: View {
    let item: Item2
    var body: some View {
        Image(item.imageName)
    }
}

struct ItemView: View {
    let item: Item
    
    var body: some View {
        switch item {
        case let .item1(item): Item1View(item: item)
        case let .item2(item): Item2View(item: item)
        }
    }
}

struct ContentView: View {
    var items: [Item]
    
    var body: some View {
        ForEach(items) { item in
            ItemView(item: item)
        }
    }
}

and with UIKit it could be totally different.

1 Like

Thank you!

If you want to expand your UI rendered to also have an ECS API in Swift, you may also want to look at Apple’s RealityKit framework.

Other than that 99% of the times you want to use a struct, with protocols for abstraction. If you want to learn more about the 1% where classes are used, here’s a talk discussing Swift’s API design.