Search code examples
swiftrealmgameplay-kit

How to save and load GKGameModelPlayer from Realm in Swift?


I am attempting to implement a GKGameModel in my application. In it, it holds variables to a few things, but for the purposes of my question I'm interested in the following two variables:

import GameplayKit
final class GameModel: NSObject, GKGameModel {
    var players: [GKGameModelPlayer]?
    var activePlayer: GKGameModelPlayer?
}

I do something like this to initialise the game with 3 players (not exact)

let game = GameModel.init()
game.players = [Player(),Player(),Player()] // Create 3 players
guard let firstPlayer = game.players.first else {
     return
}
game.activePlayer = firstPlayer

A player class is defined as:

class Player : NSObject, GKGameModelPlayer {
    var playerId: Int // GKGameModelPlayer protocol variable
    let name: String
    var cash: Int = 0
}

In my project I have Realm Entities and the models seperated. So there will be a PlayerEntity and a Player class.

I'm wanting to use RealmSwift to save and load the GKGameModelPlayer data, and more specifically the ability to store/re-store the active player.

I think the key here is the playerId variable; but I am not sure.

But what I'm not sure about is retrieving this information and then re-mapping it into a valid GKGameModelPlayer format

My current idea/theory is that I need to map my model to an entity class and vice-versa.

Ie:

// [REALM] Player entity
class PlayerEntity: Object {
    @objc dynamic var id = UUID().uuidString
    @objc dynamic var playerId: Int = 0
    @objc dynamic var name: String = ""
    @objc dynamic var cash: Int = 0

    override static func primaryKey() -> String {
       return "id"
    }
}

And then I extend this class to do some "mapping":

extension PlayerEntity {
    // Map model -> entity
    convenience init(model: Player) {
        self.init()

        self.playerId = model.playerId
        self.name = model.name
        self.cash = model.cash
    }
}

extension Player {
    // Map entity -> model
    convenience init(entity: PlayerEntity) {
        let playerId = entity.playerId
        let name = entity.name
        let cash = entity.cash

        self.init(id: playerId, name: name, cash: cash)
    }
}

Right now, the playerId is always zero (0) because I'm not really sure how to set it.

I can save a player to realm.

The issue comes from when I try to restore the player, and I want to restore the activePlayer variable in the GameModel

Therefore, my question is:

How would I go about saving and restoring the activePlayer variable so that it continues to comply to GKGameModelPlayer?

I appreciate any assistance on this.

With thanks


Solution

  • While you could use those extensions, sometimes simpler is better. Here's a rough example:

    class PlayerEntity: Object {
        @objc dynamic var playerId: Int = 0
        @objc dynamic var name: String = ""
        @objc dynamic var cash: Int = 0
    
        convenience init(withPlayer: PlayerClass) {
           self.init()
           self.playerId = withPlayer.playerId
           self.name = withPlayer.name
           self.cash = withPlayer.cash
        }
    
        func getPlayer() -> Player {
           let p = Player()
           p.playerId = self.playerId
           p.name = self.name
           p.cash = self.cash
           return p
        }
    
        override static func primaryKey() -> String {
           return "playerId"
        }
    }
    

    to load all the players into an array... this will do it

    let playerResults = realm.objects(PlayerEntity.self)
    for player in playerResults {
       let aPlayer = player.getPlayer()
       self.playerArray.append(aPlayer)
    }
    

    Notice the removal of

    @objc dynamic var id = UUID().uuidString
    

    because it's not really being used to identify the object as a primary key.

    The primary key is really

    var playerId: Int // GKGameModelPlayer protocol variable
    

    which is fine to use as long as it's unique.