5

I'm trying to create my first sprite kit swift game, and I have my game data working using nsuserdefaults. I want to make it more safe so I'm attempting to transition to NSCoding, but nothing I've found online has helped me completely and has just confused me (so sorry if an answer already exists, I probably already looked at it and wasn't able to understand.)

This is my GameState class:

class GameState {
var score: Int
var highScore: Int
var stars: Int
var sound: Bool
var carType: Int
var xSize: Double
var ySize: Double
var gameOver: Bool

class var sharedInstance: GameState {
    struct Singleton {
        static let instance = GameState()
    }

    return Singleton.instance
}

init() {
    // Init
    score = 0
    highScore = 0
    stars = 0
    carType = 1
    sound = true
    xSize = 1
    ySize = 1
    gameOver = false

    // Load game state
    let defaults = NSUserDefaults.standardUserDefaults()

    highScore = defaults.integerForKey("highScore")
    stars = defaults.integerForKey("stars")
    sound = defaults.boolForKey("sound")
    carType = defaults.integerForKey("carType")
    xSize = defaults.doubleForKey("xSize")
    ySize = defaults.doubleForKey("ySize")
    gameOver = defaults.boolForKey("gameOver")
}


func saveState() {
    // Update highScore if the current score is greater
    highScore = max(score, highScore)

    // Store in user defaults
    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setInteger(highScore, forKey: "highScore")
    defaults.setInteger(stars, forKey: "stars")
    defaults.setBool(sound, forKey: "sound")
    defaults.setInteger(carType, forKey: "carType")
    defaults.setDouble(xSize, forKey: "xSize")
    defaults.setDouble(ySize, forKey: "ySize")
    defaults.setBool(gameOver, forKey: "gameOver")
    NSUserDefaults.standardUserDefaults().synchronize()
} }

and I save and load data in my other classes using:

GameState.sharedInstance.*****

How would I go about changing this to implement NSCoding and then load and save the data from other classes? Thanks.

AEkon
  • 119
  • 6

1 Answers1

2

The class you want to save has to conform to the NSCoding protocol and implement required init(coder aDecoder: NSCoder) and func encodeWithCoder(aCoder: NSCoder).

Here's a simplified example:

class GameState: NSObject, NSCoding {

    var score: Int?
    var gameOver: Bool?

    init(score: Int, gameOver: Bool) {
        super.init()
        self.score = score
        self.gameOver = gameOver
    }

    required init(coder aDecoder: NSCoder) {
        self.score = aDecoder.decodeObjectForKey("score") as? Int
        self.gameOver = aDecoder.decodeObjectForKey("gameOver") as? Bool
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.score, forKey: "score")
        aCoder.encodeObject(self.gameOver, forKey: "gameOver")
    }

}

let myGameState = GameState(score: 42, gameOver: true)

Then you can use NSKeyedArchiver and NSKeyedUnarchiver, for example in the AppDelegate, to save and load your object as data:

func applicationWillTerminate(aNotification: NSNotification) {
    NSKeyedArchiver.archiveRootObject(myGameState, toFile: "a/file/path")
}

func applicationDidFinishLaunching(aNotification: NSNotification) {
    if let data = NSKeyedUnarchiver.unarchiveObjectWithFile("a/file/path") as? GameState {
        myGameState = data
    }
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Thanks for your reply, just a few questions: is 'let myGameState...' what you use instead of the original init() class to add values to every variable? how would i update the gamestate from the other classes? (to update score and sound etc) thanks – AEkon May 31 '15 at 21:03
  • You're welcome. It's just an init with parameters I made for my example, you don't have to change yours. You only have to implement the "required" one and make sure all the properties you want to load are declared here. – Eric Aya May 31 '15 at 21:04
  • oh alright ill try that thanks again! for the toFile in the appdelegate, how would i save it to the phone being used? thanks – AEkon May 31 '15 at 21:06
  • You save it where you want (that is different from NSUserDefaults in that regard). I suggest not in the cache folder (it is cleaned regularly), better do it like in `NSDocumentDirectory` or any place which suits your app needs. – Eric Aya May 31 '15 at 21:08
  • Alright, is it ok if we go to chat because this is still very new to me and I need more specific help to my app, thanks – AEkon May 31 '15 at 21:12
  • Try this answer first, and if you have another question, create a new question on SO. :) Suggestion: if you're worried about modifying your code, try to make work my example in a blank project first, it will take you 5 minutes max. Then you will be able to adapt your real code. Then if you have to come back for another question, I will be happy to help. – Eric Aya May 31 '15 at 21:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79275/discussion-between-aekon-and-eric-d). – AEkon May 31 '15 at 21:54
  • Is this filepath real, or imaginary placeholder: `toFile: "a/file/path"` – Confused Dec 21 '16 at 12:51