Load Highscore on Main Menu of game SWIFT XCODE - ios

So I have created a game and I already know how to load and save the high score for the game and I want to know how to display the high score on the main screen (the main menu is the first view to appear) and the high score is saved on the 2nd view which is the game view is there a way to pass the data to the main menu without changing to the second view? Please help, thanks to anyone who replies!

You can use NSUserdefaults for this task. In your second view set your Highscore value into NSUserdefaults like below,
NSUserDefaults.standardUserDefaults().setObject(YourValue, forKey:"HighScore")
NSUserDefaults.standardUserDefaults().synchronize()
And then in your first view's viewdidload method just add this,
if(NSUserDefaults.standardUserDefaults().objectForKey("HighScore") as! String != "") {
self.yourLabelName.text = HigheScore
} else {
print("No highscore set.")
}

Create a class like below class that has at least two methods saveHighScore and readHighScore and in all viewControllers and also all other classed that you want to access the score create an instance of this class and call the methods you want.
Swift
public class ScoreManager: NSObject {
let highScoreDefaultKey = "HighScoreDefaultKey"
public func saveHighScore (highScore: NSNumber) {
//This method includes your implementation for saving the high score
//You can use UserDefault or any other data store like CoreData or
//SQLLit or etc. For example I added UserDefault
NSUserDefaults.standardUserDefaults().setObject(highScore, forKey: highScoreDefaultKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
public func getHighScore() -> NSNumber?
{
//This method includes your implementation for reading the high score
let highScore = NSUserDefaults.standardUserDefaults().objectForKey(highScoreDefaultKey)
if (highScore != nil) {
return highScore as! NSNumber?;
}else
{
//No high score availble, so return nil
return nil;
}
}
}
usage:
var highScore = ScoreManager().getHighScore()
ScoreManager().saveHighScore(NSNumber(int: 120))
Objective-C
#import "ScoreManager.h"
#define USERDEFAULTS_HIGHSCRORE_KEY #"highScore"
#implementation ScoreManager
- (void) saveHighScore:(NSNumber *) highScore
{
//This method includes your implementation for saving the high score
//You can use UserDefault or any other data store like CoreData or
//SQLLit or etc. For example I added UserDefault
[[NSUserDefaults standardUserDefaults] setObject:highScore forKey:USERDEFAULTS_HIGHSCRORE_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSNumber *) readHighScore
{
//This method includes your implementation for reading the high score
NSNumber *highScore = [[NSUserDefaults standardUserDefaults] objectForKey:USERDEFAULTS_HIGHSCRORE_KEY];
if (highScore) {
return highScore;
}else
{
//No high score availble, so return nil
return nil;
}
}
#end
The advantage are that:
1- The implementation of your read and write process is only in one place and you can change it (for example switch from UserDefaults to CoreData) whenever you wants and do not worry about to change all places that you are working with Score
2- Simply call only one method when you want to access to score or write it.
3- Simply debug it when you see a bug or something like this.
Notice: If you are worry about synchronization it is better to use a singleton class that manages this matter.

Related

How to get data from one class to viewController?

Unfortunately I m newbie and cant solve this problem for days. the problem is I cant success to gather score from game engine class to view controller so that I can show it with a label.
here is I want to extract data from this func to global variables.
private func nextMove() {
current = .random(config)
if (well.hasCollision(current)) {
stopTimer()
scene.showGameOver(scores)
//extract score to another variable can be reached by another class
} else {
scene.show(current)
}
}
I tried many methods, creating a global class with init method, creating struct, creating protocol..etc that already written here, but never successed. any idea will be appriciated. thanks..
A Singleton could be what you're looking for.
Here's an example:
class ScoresSingleton {
static let sharedSession = ScoresSingleton()
var scores : Int = 0
}
You may then use globally by:
// Setting
ScoresSingleton.sharedSession.scores = 10
// Getting
var scores = ScoresSingleton.sharedSession.scores
Hope that helps.

Setting a computed value (struct vs class)

I made a simple struct that handles the management of the UI background (the user can choose to use a gradient or image). Inside this struct is a computed property called preference which gets and sets the user's preference to UserDefaults.
When I try to set the preference property using the following code:
Background().preference = .gradient
I get an error: "Cannot assign to property: function call returns immutable value"
I have to use this instead:
var background = Background()
background.preference = .gradient
I would prefer not having to assign an instance of Background to a variable before finally setting the property.
I've found that changing Background from a struct to a class allows me to set the property using Background().preference = .gradient directly.
Can anyone give me some insight as to why this occurs? Is using class better than using struct for this situation or does it not matter?
struct Background {
enum Choice {
case gradient
case image
}
var preference: Choice {
get {
if let type = UserDefaults.standard.value(forKey: "background_type"), type as! String == "background" {
return .image
}
return .gradient
}
set(value){
if value == .image {
UserDefaults.standard.setValue("background", forKey: "background_type")
}else{
UserDefaults.standard.setValue("gradient", forKey: "background_type")
}
}
}
You're not really getting any value from making an instance of a struct / class to just wrap UserDefaults. It's a very common problem and there are lots of clever solutions out there on google if you search around. For a really simple example you could just extend UserDefaults
//: Playground - noun: a place where people can play
import Cocoa
enum BackgroundChoice {
case gradient
case image
}
extension UserDefaults {
var backgroundChoice: BackgroundChoice {
get {
if let type = string(forKey: "background_type"), type == "image" {
return .image
}
return .gradient
}
set(value){
if value == .image {
setValue("background", forKey: "background_type")
}else{
setValue("gradient", forKey: "background_type")
}
}
}
}
UserDefaults.standard.backgroundChoice = .image
I know this doesn't answer your exact question, but I think you'll find there are better solutions to this problem if you dig around.

saving coins variable in swift 3 [duplicate]

This question already has an answer here:
Swift 3.0 save a score with SpriteKit
(1 answer)
Closed 6 years ago.
i am kinda new to swift and i need help with saving an int variable that holds the user's coins that they collect and saving it even after the app closes , so here is what i did , i have two scenes one represents startMenu which has the a struct that has the coins variable ( so i can easily control it from another scene ) , and the other scene which is GameScene , and in GameScene every time the user interacts with a coin node , it adds 1 to the coins variable in StarMenu Scene
here in my struct in StartMenu
struct Variables {
static var CoinsCollected = 0
}
and here is what i did to make it be saved
let defaults = UserDefaults.standard
defaults.set(Variables.CoinsCollected, forKey: "CoinsCollected")
defaults.synchronize()
and i have this line in GameScene inside my didbegin contact , when the user interacts with a coin
Variables.CoinsCollected += 1
and i have this line that updates the labelnode for the coins variable
coinsCollectedLabel = SKLabelNode(text: "coins: \(Variables.CoinsCollected)")
and everything works fine except that it doesn't get saved and i know i am missing something and i tried reading a lot of people's problem with the same issue but it didn't work, so if any of you guys could help me i would be highly appreciate it
You can do this in your Variables class so your coins are always saved in a persistent file:
class Variables {
static let userDefaults = UserDefaults.standard
struct Constants {
static let CoinsCollectedKey = "CoinsCollected"
}
static var CoinsCollected: Int {
get {
return userDefaults.integer(forKey: self.Constants.CoinsCollectedKey)
}
set(value) {
userDefaults.setValue(value, forKey: self.Constants.CoinsCollectedKey)
}
}
}
let defaults = UserDefaults.standard
...
//after Initializing Variables Instance
//get saved value
guard Variables.CoinsCollected = defaults.integer(forKey: "CoinsCollected") else {
print("Previously saved coins don't exist")
//create UserDefaults value for coins
defaults.set(Variables.CoinsCollected, forKey: "CoinsCollected")
}
I think it would be worth saving your entire Variables object instead, so that you can get any new values you add to your struct later - in which case you should serialize your Variables object using (for example)
//set object
defaults.set(Variables, forKey: "Variables")
//read object
let variables: Variables = defaults.object(forKey: "Variables")
//theoretically you should now be able to get CoinsCollected value within your retrieved object~
//e.g.
print(variables.CoinsCollected)

What if I want to assign a property to itself?

If I attempt to run the following code:
photographer = photographer
I get the error:
Assigning a property to itself.
I want to assign the property to itself to force the photographer didSet block to run.
Here's a real-life example: In the "16. Segues and Text Fields" lecture of the Winter 2013 Stanford iOS course (13:20), the professor recommends writing code similar to the following:
#IBOutlet weak var photographerLabel: UILabel!
var photographer: Photographer? {
didSet {
self.title = photographer.name
if isViewLoaded() { reload() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
reload()
}
func reload() {
photographerLabel.text = photographer.name
}
Note: I made the following changes: (1) the code was switched from Objective-C to Swift; (2) because it's in Swift, I use the didSet block of the property instead of the setPhotographer: method; (3) instead of self.view.window I am using isViewLoaded because the former erroneously forces the view to load upon access of the view property; (4) the reload() method (only) updates a label for simplicity purposes, and because it resembles my code more closely; (5) the photographer IBOutlet label was added to support this simpler code; (6) since I'm using Swift, the isViewLoaded() check no longer exists simply for performance reasons, it is now required to prevent a crash, since the IBOutlet is defined as UILabel! and not UILabel? so attempting to access it before the view is loaded will crash the application; this wasn't mandatory in Objective-C since it uses the null object pattern.
The reason we call reload twice is because we don't know if the property will be set before or after the view is created. For example, the user might first set the property, then present the view controller, or they might present the view controller, and then update the property.
I like how this property is agnostic as to when the view is loaded (it's best not to make any assumptions about view loading time), so I want to use this same pattern (only slightly modified) in my own code:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
photographerLabel?.text = photographer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
photographer = photographer
}
Here instead of creating a new method to be called from two places, I just want the code in the didSet block. I want viewDidLoad to force the didSet to be called, so I assign the property to itself. Swift doesn't allow me to do that, though. How can I force the didSet to be called?
Prior to Swift 3.1 you could assign the property name to itself with:
name = (name)
but this now gives the same error: "assigning a property to itself".
There are many other ways to work around this including introducing a temporary variable:
let temp = name
name = temp
This is just too fun not to be shared. I'm sure the community can come up with many more ways to do this, the crazier the better
class Test: NSObject {
var name: String? {
didSet {
print("It was set")
}
}
func testit() {
// name = (name) // No longer works with Swift 3.1 (bug SR-4464)
// (name) = name // No longer works with Swift 3.1
// (name) = (name) // No longer works with Swift 3.1
(name = name)
name = [name][0]
name = [name].last!
name = [name].first!
name = [1:name][1]!
name = name ?? nil
name = nil ?? name
name = name ?? name
name = {name}()
name = Optional(name)!
name = ImplicitlyUnwrappedOptional(name)
name = true ? name : name
name = false ? name : name
let temp = name; name = temp
name = name as Any as? String
name = (name,0).0
name = (0,name).1
setValue(name, forKey: "name") // requires class derive from NSObject
name = Unmanaged.passUnretained(self).takeUnretainedValue().name
name = unsafeBitCast(name, to: type(of: name))
name = unsafeDowncast(self, to: type(of: self)).name
perform(#selector(setter:name), with: name) // requires class derive from NSObject
name = (self as Test).name
unsafeBitCast(dlsym(dlopen("/usr/lib/libobjc.A.dylib",RTLD_NOW),"objc_msgSend"),to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(class_getMethodImplementation(type(of: self), #selector(setter:name)), to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(method(for: #selector(setter:name)),to:(#convention(c)(Any?,Selector,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
_ = UnsafeMutablePointer(&name)
_ = UnsafeMutableRawPointer(&name)
_ = UnsafeMutableBufferPointer(start: &name, count: 1)
withUnsafePointer(to: &name) { name = $0.pointee }
//Using NSInvocation, requires class derive from NSObject
let invocation : NSObject = unsafeBitCast(method_getImplementation(class_getClassMethod(NSClassFromString("NSInvocation"), NSSelectorFromString("invocationWithMethodSignature:"))),to:(#convention(c)(AnyClass?,Selector,Any?)->Any).self)(NSClassFromString("NSInvocation"),NSSelectorFromString("invocationWithMethodSignature:"),unsafeBitCast(method(for: NSSelectorFromString("methodSignatureForSelector:"))!,to:(#convention(c)(Any?,Selector,Selector)->Any).self)(self,NSSelectorFromString("methodSignatureForSelector:"),#selector(setter:name))) as! NSObject
unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setSelector:")),to:(#convention(c)(Any,Selector,Selector)->Void).self)(invocation,NSSelectorFromString("setSelector:"),#selector(setter:name))
var localVarName = name
withUnsafePointer(to: &localVarName) { unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setArgument:atIndex:")),to:(#convention(c)(Any,Selector,OpaquePointer,NSInteger)->Void).self)(invocation,NSSelectorFromString("setArgument:atIndex:"), OpaquePointer($0),2) }
invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
}
}
let test = Test()
test.testit()
There are some good workarounds but there is little point in doing that.
If a programmer (future maintainer of the code) sees code like this:
a = a
They will remove it.
Such a statement (or a workaround) should never appear in your code.
If your property looks like this:
var a: Int {
didSet {
// code
}
}
then it's a not a good idea to invoke the didSet handler by assignment a = a.
What if a future maintainer adds a performance improvement to the didSet like this?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
The real solution is to refactor:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
And instead of a = a directly call updateA().
If we are speaking about outlets, a suitable solution is to force the loading of views before assigning for the first time:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
_ = self.view // or self.loadViewIfNeeded() on iOS >= 9
photographerLabel?.text = photographer.name // we can use ! here, it makes no difference
}
}
That will make the code in viewDidLoad unnecessary.
Now you might be asking "why should I load the view if I don't need it yet? I want only to store my variables here for future use". If that's what you are asking, it means you are using a view controller as your model class, just to store data. That's an architecture problem by itself. If you don't want to use a controller, don't even instantiate it. Use a model class to store your data.
I hope one day #Swift developers will fix this miscuzzi :)
Simple crutch:
func itself<T>(_ value: T) -> T {
return value
}
Use:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(optionals including)
Make a function that the didSet calls then call that function when you want to update something? Seems like this would guard against developers going WTF? in future
#vacawama did a great job with all those options. However in iOS 10.3, Apple banned some of these ways and most likely will be doing it in the future again.
Note: To avoid the risk and future errors, I will use a temporary variable.
We can create a simple function for that:
func callSet<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: callSet(&foo)
Or even a unary operator, if there is a fitting one ...
prefix operator +=
prefix func +=<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: +=foo

Wait on thread started from another class - Objective C

I have encountered a threading issue I cannot solve. I want to perform a large Core Data save operation of about 12000 objects on a separate thread in a certain class, and in another class control a button action in relation with the save operation being finished. What is the best approach on this?
This is how the save operation looks like:
Class A
-(void) saveAsync
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
//arrayOfObjects holds the 12000 objects
for(aClass *object in arrayOfObjects)
{
[self saveToCoreData: object];
}
NSLog(#"Finished saving");
});
}
-(void) saveToCoreData : (aClass *) object
{
//perform save operation here
}
And this is the action method on my button (which is really nothing yet)
Class B
-(IBAction) buttonActionMethod
{
//take different actions depending on the objects being persisted to the store or still saving
}
I am asking for a solution that would allow me to know if the objects are saved at a button press. The code I provided is just a raw example to express the idea, I don't expect it to work like that. I have thought of using NSOperationQueue or create threads or use groups, but I have not found a solution that works.
Thank you in advance!
You need to store a BOOL on Class A, which will indicate the state of saving,
and check that BOOL from Class B, and make different actions.
>> Class A
#property (atomic) BOOL isSaving;
- (void)saveAsync
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//arrayOfObjects holds the 12000 objects
self.isSaving = YES;
for(aClass *object in arrayOfObjects) {
[self saveToCoreData:object];
}
self.isSaving = NO;
NSLog(#"Finished saving");
});
}
- (void)saveToCoreData:(aClass *)object
{
//perform save operation here
}
>> Class B
- (IBAction)buttonActionMethod
{
// take different actions depending on the objects being persisted to the store or still saving
if (classA.isSaving) {
} else {
}
}
You can subscribe to NSManagedObjectContextDidSaveNotification:
Posted whenever a managed object context completes a save operation.
The notification object is the managed object context. The userInfo
dictionary contains the following keys: NSInsertedObjectsKey,
NSUpdatedObjectsKey, and NSDeletedObjectsKey.
I strongly recommend NOT to use any class variables, as they store class state which is not a real part of it

Resources