Last active
March 6, 2022 13:12
-
-
Save veeneck/5521e0b6381051b8387f48ba792ba888 to your computer and use it in GitHub Desktop.
Saving a SpriteKit game in Swift 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 _ { | |
} | |
} | |
} |
Code using the class: https://gist.github.com/veeneck/1d988af6e20e67b131c9cf775f546f16
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.
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
See this gist for Level data: https://gist.github.com/veeneck/96306731570687e76d1190fa6ccacf0c