July 2, 2014

Generics in Swift for Entity-Component-Systems

I have revised the entity class and added a component getter that uses Swift generics to eliminate the need for type casting the result. This will lead to more concise code in most cases, and makes it less likely for us to make mistakes when using the engine to build a game.

Previously, components were accessed by passing a `type: String` to the entity’s `get` method. The entity would return an object of type `LGComponent?`, that is, an optional component of no specific type. The result was that we had to access components like this:

var positionComponent = entity.get(LGPosition.type()) as? LGPosition

If we wanted to unwrap it using optional binding in Swift, we still had to cast it:

if let positionComponent = entity.get(LGPosition.type()) as LGPosition
{
	// do stuff
}

What we want instead is to make that more concise, not forcing a developer to repeat himself. Here’s what we want to be able to do to access a component:

var positionComponent = entity.get(LGPosition)

Until recently, I thought that such functionality was either not possible in Swift or (at least) not possible yet. Swift does not allow us to use `Class` as the type of a function parameter, like we might do in Java. We cannot even use Swift’s own Metatype Type, as far as I can tell. However, I found something that does work — using generics and making the parameter of type `T.Type` like this:

func get(type: T.Type) -> T?
{
	return components[type.type()] as? T
}

This is the first time I have been able to make practical use of the Metatype Type, and it works exactly as expected. I have already updated the GitHub repository and all calls to the `get` method have been made more concise. There are still a couple of puzzles I have not been able to solve in Swift:

  • Variadic Parameters, as in `get(type: Metatype…)`, will not work with this approach.
  • Components still require an explicit `type()` to be defined, because the Swift’s `Metatype` can’t be hashed as a dictionary key like Objective-C’s `id` could.

If you know how to do either of those things, let me know in the comments! Check out the source code and tell me what you think! I am still working through how to implement tile collisions most efficiently without resorting to giving tiles actual physics bodies.

Posted by Luke Godfrey

Luke Godfrey is a graduate student at the University of Arkansas, pursuing a PhD in Computer Science. He has created applications on a number of platforms, and is especially interested in mobile and web development.

View more posts from this author

6 thoughts on “Generics in Swift for Entity-Component-Systems

  1. David Moles

    It’s annoying that Swift doesn’t seem to have an equivalent of System.identityHashCode(). Do type values adopt Printable? (Not in front of my Mac right now or I’d look myself.) If so maybe you could use the description as a key — although that might turn out to be expensive at runtime.

     
    Reply
    1. Luke Godfrey

      I completely agree. I wish there was a good way to hash a Type without having to explicitly give the Type a hashing function as I have done. Unfortunately, there appears to be no reflection or introspection in Swift at the moment; println(MyClass.self) prints (Metatype) for all classes.

       
      Reply
      1. David Moles

        Got a brief chance to poke into this again and found that NSStringFromClass seems to work, although only for class types:


        > class Foo {}
        > NSStringFromClass(Foo.self)
        $R1: String! = "_TtC12lldb_expr_413Foo"

        If you try it with a protocol or a built-in type it will complain:


        > protocol Bar {}
        > NSStringFromClass(Bar.self)
        :43:19: error: type 'Bar' does not conform to protocol 'AnyObject'

        Not perfect, but still more flexible than requiring everything to extend LGComponent

         
        Reply
        1. Luke Godfrey

          Does that work with Types that do not inherit from NSObject? That would be very nice to not have to explicitly include the class name in every component. Does it also work to find the class name from an instance? For example:

          var foo = Foo()
          NSStringFromClass(foo.dynamicType)

          As soon as I get back to my Mac I’ll test it out.

           
          Reply

Leave a Reply

Your email address will not be published. Required fields are marked *