Search code examples
swiftswift3option-typegameplay-kit

Can I make self.entity a non optional for certian functions in a GKComponent using swift?


I'm using the GameplayKit entity component system to develop a game.

I have a number of GKComponent. Most of the instance functions require that self.entity is not nil, but it is an optional, as a component may be initialised before being attached to an entity.

If I "do things properly", I understand I should use guard (or if) to check that self.entity is not nil, for each function where I require it. This adds a chunk of extra code for each function, and in addition a function may require the entity has specific components to function.

Problem statement:

For some functions in a GKComponent, I want to specify that the function requires self.entity is not nil, and has some specific components, without needing a number of guard or if statements which bulks up my code.

Things I've tried:

I tried creating an extension to GKComponent. The extension contains a function which throws an error if the entity is nil, returns true if it is not nil.

import Foundation
import GameplayKit

extension GKComponent {

  enum ComponentExtensionError: Error {
    case hasNoEntity
  }

  func requiresEntity() throws -> Bool {
    if (self.entity != nil) {
      return true
    } else {
      throw ComponentExtensionError.hasNoEntity
    }
  }

}

GKComponent function:

func setTileWalls () {
    try? self.requiresEntity()
    # Some stuff
}

This compiles, but doesn't solve my problem, as I still have to access the entity as self.entity?.somefunc(). I figured now I know that self.entity is not nil, I could proceed to set it as unwrapped...

func setTileWalls () {
    try? self.requiresEntity()
    self.entity = self.entity!
    # Some stuff
}

But alas, you cannot SET self.entity.

I considered I could in stead modify the extension to return the entity, and then do something like...

func stTileWalls () {
    guard let entity = self.requiresEntity() else { throw }
}

but then I'd need to modify each functions code to use entity rather than self.entity. I don't feel like this is optimal solution, but maybe an acceptable one.

My ideal solution would be to be able to call a function which checks that self has an entity, and some components, making them non optional for the scope, and throwing an error if any are nil.

Why? I'm frustrated by entity and any component always being optional (when I know it shouldn't be). If I understand correctly, "doing things properly" requires that I guard and throw (or return maybe?). Syntactically I feel it looks ugly and I feel like I'm missing something in terms of fixing this in an elegent way with Swift.


Solution

  • There is no easy solution to solve this issue if you want a safe solution that requires less boilerplate code than unwrapping an optional. Throwing an error instead of returning nil is definitely a bad idea, since handling an error requires even more boilerplate code than safely unwrapping an optional.

    You cannot override existing variables types, so you cannot make GKComponent.entity non-optional either.

    Use guard statements for optional binding, that way you can minimise the boilerplate code for unwrapping the value.