Instantiating instances of type at runtime?

I want to write a “lite” version of the Unity environment for Swift and SceneKit. One thing I’d like to be able to do is instantiate a struct type that conforms to a protocol.

Xcode does this with its SwiftUI previews, although you have to write a bit of code to do it (presumably so you can hook into the instantiation):

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Somehow Xcode can find ContentView_Previews and create an instance of it. Does anyone know how it does that?

1 Like

Right, I had heard it might use SourceKit to do this. Okay, so skipping over that for now, that is, assuming I use SourceKit to do the same, how might I instantiate the class? (I haven't looked at SourceKit; maybe this is obvious.)

Xcode compiles the class and then somehow loads the code, not sure how. In my scenario the code could be already loaded, so we can assume it's available.

Update: Looking at SourceKit, ugh, I'm not sure I want to go down this road. I wish Swift had proper reflection.

Maybe check out Sourcery – here's the documentation for writing templates.

You could do something like:

for type in types.implementing["YourProtocol"] {
    /// instantiate your objects
}
2 Likes

You might check out Echo. It offers pretty powerful reflection based on Swift's stable ABI and there's good community discussion around its capabilities. I've been using it to create a version of Codable specifically for XML and so far it can do everything Codable does, including instantiate objects at runtime.

2 Likes

I do this kind of thing with a little type registry (a singleton). At the start of my program I register all the types that I want to instantiate or auto generate property views for. While registering the type we have full access to the type. That's my chance to register things like coders for runtime serialization and property view generation. It's also the right time to register a little closure to instantiate the type. For example:

componentRegistry.register { Velocity() }
componentRegistry.register { Position(.zero) }
componentRegistry.register { Rotation() }

The register function looks like this:

public mutating func register<T>(initializer: @escaping () -> T) where T: Component {
   components[ObjectIdentifier(T.self)] = ... 
}

It's not as elegant as type reflection in Unity or Unreal but it's just a few lines of extra code and simple and easy to do. I look forward to Swift someday having metaprogramming.

2 Likes

The Swift language and runtime provide primitive operations you can use to instantiate a type by name and query its conformance to protocols. If you know the mangled name of the type, you can use _typeByName("mangled name") to get the type as an Any.Type, and then cast it as? Protocol.Type to get access to its methods conforming to protocols.

4 Likes

Echo looks great!

1 Like