Search code examples
iosswiftsaveplistnscoding

NSCoding gamedata not saved swift


At the moment, my data does not get retrieved in another new session and it uses the default values. Do I have to have an existing plist file for this to work? I tried using an existing file with no luck.

I followed the guide here http://battleofbrothers.com/sirryan/saving-game-data-in-spritekit

class GameData : NSObject, NSCoding {

/// Data to save

var variableC : Int = 3

/// Create of shared instance

class var sharedInstance: GameData {

    struct Static {
        static var instance: GameData?
        static var token: dispatch_once_t = 0
    }

    dispatch_once(&Static.token) {
        var gamedata = GameData()
        if let savedData = GameData.loadGame() {
            gamedata = savedData
        }
        Static.instance = gamedata
    }

    return Static.instance!
}

override init() {
    super.init()
}

required init(coder: NSCoder) {
    super.init()
}

func encodeWithCoder(coder: NSCoder) {
    coder.encodeObject(GameData.sharedInstance, forKey: "GameData")
}

class func loadGame() -> GameData? {
    // load existing high scores or set up an empty array
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let documentsDirectory = paths[0] as! String
    let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
    let fileManager = NSFileManager.defaultManager()

    // check if file exists
    if !fileManager.fileExistsAtPath(path) {
        // create an empty file if it doesn't exist
        println("File doesn't exist")
        if let bundle = NSBundle.mainBundle().pathForResource("DefaultFile", ofType: "plist") {
            fileManager.copyItemAtPath(bundle, toPath: path, error:nil)
        }
    }

    if let rawData = NSData(contentsOfFile: path) {
        // do we get serialized data back from the attempted path?
        // if so, unarchive it into an AnyObject, and then convert to an array of HighScores, if possible
        if let data = NSKeyedUnarchiver.unarchiveObjectWithData(rawData) as? GameData {
            println("We loaded the data!")
            return data
        }
    }
    println("we returned Nil")
    return nil
}

func save() {
    // find the save directory our app has permission to use, and save the serialized version of self.scores - the HighScores array.
    let saveData = NSKeyedArchiver.archivedDataWithRootObject(GameData.sharedInstance);
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray;
    let documentsDirectory = paths.objectAtIndex(0)as! NSString;
    let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist");

    saveData.writeToFile(path, atomically: true);
}

}

I load the gamedata in the init function with this

var gameData: GameData = GameData.sharedInstance

Updating the data

    gameData.variableC = gameData.variableC + 1
    gameData.save()
    println(gameData.variableC)

Solution

  • Like Jozsef said, I forgot to decode the data. However I need to decode each of the individual variables and then copy it over to the GameData for it to work.

    Here's

    class GameData : NSObject, NSCoding {
    
    /// Data to save
    
    var variableC : Int! = 3
    
    /// Create of shared instance
    
    class var sharedInstance: GameData {
    
        struct Static {
            static var instance: GameData?
            static var token: dispatch_once_t = 0
        }
    
        dispatch_once(&Static.token) {
            var gamedata = GameData()
            if let savedData = GameData.loadGame() {
                gamedata.variableC = savedData.variableC
            }
            Static.instance = gamedata
        }
    
        return Static.instance!
    }
    
    override init() {
        super.init()
    }
    
    required init(coder: NSCoder) {
        super.init()
        self.variableC = coder.decodeObjectForKey("variableC") as? Int
    
    }
    
    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(GameData.sharedInstance.variableC, forKey: "variableC")
    }
    
    class func loadGame() -> GameData? {
        // load existing high scores or set up an empty array
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let documentsDirectory = paths[0] as! String
        let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
        let fileManager = NSFileManager.defaultManager()
    
        // check if file exists
        if !fileManager.fileExistsAtPath(path) {
            // create an empty file if it doesn't exist
            println("File doesn't exist")
            if let bundle = NSBundle.mainBundle().pathForResource("DefaultFile", ofType: "plist") {
                fileManager.copyItemAtPath(bundle, toPath: path, error:nil)
            }
        }
    
        if let rawData = NSData(contentsOfFile: path) {
            // do we get serialized data back from the attempted path?
            // if so, unarchive it into an AnyObject, and then convert to an array of HighScores, if possible
            if let data = NSKeyedUnarchiver.unarchiveObjectWithData(rawData) as? GameData {
                println("We loaded the data!")
                return data
            }
        }
        return nil
    }
    
    func save() {
        // find the save directory our app has permission to use, and save the serialized version of self.scores - the HighScores array.
        let saveData = NSKeyedArchiver.archivedDataWithRootObject(GameData.sharedInstance);
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray;
        let documentsDirectory = paths.objectAtIndex(0)as! NSString;
        let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist");
    
        saveData.writeToFile(path, atomically: true);
    }
    

    }