Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@veeneck
Last active March 6, 2022 13:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save veeneck/5521e0b6381051b8387f48ba792ba888 to your computer and use it in GitHub Desktop.
Save veeneck/5521e0b6381051b8387f48ba792ba888 to your computer and use it in GitHub Desktop.
Saving a SpriteKit game in Swift 3
import SpriteKit
class GameData : NSObject, NSCoding {
/// Current game slot being played "One" to "Four"
var currentSaveFile : String? = nil
/// A string to save
var variableA : String = "AString"
/// Reference to LevelData array which contains both static and saved data
var levelData : LevelData?
/// Money in the bank
var bank : Int = 200
/// Units available to recruit
var food : Int = 0
/// Create of shared instance
internal static let sharedInstance = GameData()
// MARK: Init
override init() {
super.init()
/// Create default data for vairables that havent been set
self.levelData = LevelData()
}
required init?(coder: NSCoder) {
super.init()
self.variableA = coder.decodeObject(forKey: "variableA") as! String
self.bank = coder.decodeInteger(forKey: "bank")
self.food = coder.decodeInteger(forKey: "food")
self.levelData = coder.decodeObject(forKey: "levelData") as? LevelData
}
func encode(with coder: NSCoder) {
coder.encode(GameData.sharedInstance.variableA, forKey: "variableA")
coder.encode(GameData.sharedInstance.bank, forKey: "bank")
coder.encode(GameData.sharedInstance.food, forKey: "food")
coder.encode(GameData.sharedInstance.levelData, forKey: "levelData")
}
func resetData() {
self.bank = 200
self.food = 0
self.variableA = "AString"
self.levelData = LevelData()
/// Even though same static instance of GameData, this flag controls which save file we're dealing with
self.currentSaveFile = nil
}
// MARK: Loading and saving
/// This loads an existing game, or starts a new slot for a new game.
func loadGame(saveFile:String) -> Bool {
/// Make sure we're not holding on to past game data
self.resetData()
/// Specifcy save file to work with
self.currentSaveFile = saveFile
/// If save file exists already, load it in and assign variables
if let savedData = self.loadGameData(file:saveFile) {
self.levelData = savedData.levelData
self.food = savedData.food
self.bank = savedData.bank
self.variableA = savedData.variableA
return true
}
return false
}
/// Internal method to actual load the file
private func loadGameData(file:String) -> GameData? {
/// load existing high scores or set up an empty array
let path = GameData.getFilePath(name:file)
/// Only attempt to decode if file already existed
if GameData.fileExistsAtPath(path:path, create: true) {
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 a GameData object
if let data = NSKeyedUnarchiver.unarchiveObject(with: rawData as Data) as? GameData {
return data
}
}
}
return nil
}
func save() {
/// If we're working with an active save file, attempt to save it
if let saveFile = self.currentSaveFile {
DispatchQueue.global(attributes: .qosBackground).async {
/// find the save directory our app has permission to use, and save the serialized version of self.scores - the HighScores array.
let saveData = NSKeyedArchiver.archivedData(withRootObject: GameData.sharedInstance)
let path = GameData.getFilePath(name: saveFile)
do {
let location = URL(fileURLWithPath: path)
try saveData.write(to:location, options: .atomicWrite)
} catch _ {
print("unable to save file")
}
}
}
}
// MARK: Files
/// This would create a file in the documents directory of GameDataOne.plist
class func getFilePath(name:String) -> String {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0] as String
let fileName = "/GameData\(name)"
let path = documentsDirectory + "\(fileName).plist"
return path
}
/// Check if a file exists. Pass in create = true to create the file if it doesn't exist.
/// Requires DefaultFile.plist to be an empty plist in your project directory
class func fileExistsAtPath(path:String, create:Bool = false) -> Bool {
let fileManager = FileManager.default()
if !fileManager.fileExists(atPath: path) {
// create an empty file if it doesn't exist and create is true
if(create) {
if let bundle = Bundle.main().pathForResource("DefaultFile", ofType: "plist") {
do {
try fileManager.copyItem(atPath: bundle, toPath: path)
} catch _ {
print("Unable to create base save file")
}
}
else {
print("DefaultFile not found")
}
}
return false
}
return true
}
/// Delete a save file. Requires you to path in proper path by calling getFilePath first
class func deleteFileAtPath(path:String) {
let fileManager = FileManager.default()
do {
try fileManager.removeItem(atPath: path)
} catch _ {
}
}
}
@veeneck
Copy link
Author

veeneck commented Jul 1, 2016

@veeneck
Copy link
Author

veeneck commented Jul 1, 2016

@covmon1
Copy link

covmon1 commented Sep 18, 2016

I'm getting an error at line 107 with DispatchQueue.global(attributes: .qosBackground).async. The error is that

Argument labels '(attributes:)' do not match any available overloads

Am I doing something wrong, or is there something slightly wrong with the code? Thanks for your help.

@siemiatj
Copy link

siemiatj commented Oct 2, 2016

Use DispatchQueue.global(qos: .background).async { instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment