How to deal with lag spike from gamecenter - swift

My game uses Game Center for scoreboards. At the start of the app I ask Game Center to login.
The problem is that after a variable amount of time Game Center pops up with its "welcome back...." thing and this creates a HUGE latency spike in my app that is already low on resources.
I have played with the completion handlers trying to find out some sort of callback when the banner is done... but nothing!
How am I supposed to handle this? I have a loading screen, could I perhaps not finish loading until the thing has popped up?
Here is my code I use right now
func authenticateLocalPlayer()
{
Holder.loggedIn = false
print("Start Authenticate")
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil)
{
self.presentViewController(viewController!, animated: false, completion: {self.authenticated()})
}
}
}

I've seen the same thing, and even seen differences in login times on different devices sitting next to each other on the same network. My strategy, which I believe originally came from Apple's docs or WWDC videos but I don't have a reference handy, is to initiate authentication immediately on startup, like so at in my first view controller's viewDidAppear:
Disable the start game button so users can't attempt a session when they can't even log in.
Start authentication
Do all other initiation stuff, animations, etc.
In the authentication handler, if successful, enable the start game button
This way, I move the lag pain to the app startup, which can be somewhat masked by the normal app launch delays, startup animations and what not. When I actually launch a gaming session, I've ensured that login overhead (or failure) is out of the way.
Btw, not directly related to your question, but I noticed that in your authentication handler, you present the view controller if it's present, and assume the user is authenticated if the view controller is nil. Note that when an error occurs, the error value is set but the view controller is nil. So, a nil VC can mean the user is authenticated, but it can also mean that the authentication failed. Always check the value of error first.
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (error)
{
//check and process the error.
//bail out because we are NOT authenticated
return;
}
if (viewController != nil)
{
self.presentViewController(viewController!, animated: false, completion: {self.authenticated()})
}
}

Related

Dismissing SKScene Completely

Okay so I have an app that starts off with a menu view controller that prompts user to press one of 4ish buttons which then loads a viewcontroller which then presents a scene and the user plays the game based on which button was pressed.
I am then having the user being redirected to another viewcontroller which presents another another scene, once a condition is met (they lose the game). Only problem is, the 2nd viewcontroller(and i'm assuming it's scene) is still running. I know this because I have a print statement inside of it's override function update method to see if it's still there.
In addition, I have audio playing in my old gamesene and it's still currently playing. I wouldn't EXACTLY mind that since later on i'm going to just end up passing audio data (mute all) between all 3 viewcontrollers and their presented scenes.
Only problem with what is going on right now is that when I run the app since the old viewiewcontroller an it's scene seem to still be running underneath, it keeps calling the transition which causes a weird look where when the condition meets, the transition loops endlessly to the new viewcontroller then back to the beginning of the transition then to the new viewcontroller again.
I've tried this piece of code:
let theVC = self.viewController?.storyboard?.instantiateViewController(withIdentifier: "TrumpVC") as! TrumpViewController
self.viewController?.navigationController?.pushViewController(theVC, animated: true)
self.viewController?.dismiss(animated: true, completion: {});
But it doesn't seem to help at all :( Essentially I navigate to a new viewcontroller and dismiss the current one (this is all in my scene)
Thanks
Solution:
let theVC = self.viewController?.storyboard?.instantiateViewController(withIdentifier: "TrumpVC") as! TrumpViewController
self.viewController?.navigationController?.pushViewController(theVC, animated: true)
self.viewController?.removeFromParentViewController()
self.viewController?.dismiss(animated: true, completion: nil)
self.view?.presentScene(nil)

Change key to true then show view in swift

OK, so I've added a view onto my Application that asks the user to accept or decline the Terms of Service. I have it so when they click accept, it changes the key "TermsAccepted" to true. If they close the app, and re-open it, it gives them access. However I'd like to be able to give them access without re-opening the app first.
So in my ViewController (Main Screen), in my viewDidLoad I have the following:
if NSUserDefaults.standardUserDefaults().boolForKey("TermsAccepted") {
// They've been accepted, do nothing.
} else {
let termsView = self.storyboard?.instantiateViewControllerWithIdentifier("FirstLaunchTerms") as! FirstLaunchTermsView
self.presentViewController(termsView, animated: true, completion: null
}
In the 'LaunchTermsView' I have the following code for when they accept the terms.
#IBAction func acceptTerms(sender : AnyObject)
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "TermsAccepted")
}
And thi works fine, but they have to re-open the application.
I tried to just have it so the button opens the Main View at the same time as those terms are accepted (After the key is updated) but it gives me the following error.
fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)
I assumed this meant that it was wanting to re-open the launch terms view again, so I tried to move all the code from viewDidLoad to viewWillAppear so it checks each time, but it just gives the same error. (it was a long shot try before I posted on here).
I had a look at some posts on here, but a lot of them were in ObjC or just didn't give me a solution or any form of help to trying to find one myself.
As you're presenting your terms view controller you should simply be able to dismiss it when you're done with it. You don't show any other code so I'm assuming that your 'home' view controller is waiting to be revealed underneath (you don't need to try to show it again).

WKInterfaceLabel.setText not reflecting changes following modal viewController dismissal

I am working on a simple video game for the Apple Watch. In awakeWithContext() I call my own method, startNewGame(), and register for it to be called again when receiving a notification from my modal viewController. This all works fine. Then upon determining the game is over, I call self.presentControllerWithName().
When that controller is dismissed by the player I call self.dismissController(). Then I fire a notification that once again calls startNewGame(). This is where things get weird.
self.score = 0
let scoreString = formatScore(0) //"0000"
self.scoreboard.setText(scoreString)
let hiScore = NSUserDefaults.standardUserDefaults().integerForKey("hiScore")
let hiScoreString = formatScore(hiScore)
self.hiScoreboard.setText(hiScoreString)
The above excerpt from startNewGame() shows me resetting the score and updating both "scoreboards" both WKInterfaceLabels that present scores in a skeumorphic old-timey LCD fashion. Because of this, I call formatScore() which returns a string with leading zeros. Anyway, I then set the text on them both and… nothing happens. Both boards show the same score as before the game over view controller was shown. It is only when they are next updated in response to player's actions that they update to reflect the correct values. Because I only have issues with this when the code runs shortly following the dismissal of a modal viewController, I suspect there is some connection. Anyway, I am stumped, some help would be much appreciated.
I ran into the same problem receiving data from the iPhone using sendMessage.
My code was pretty straight forward:
dispatch_async(dispatch_get_main_queue(),{
if let error = error {
NSLog("showing error: \(error)");
self.lblTitle.setText("Error...");
}
});
For me, the message was being logged, but the interface wasn't getting updated.
Ends up I was showing multiple pages using reloadRootControllersWithNames and when I tried to update a label within a page that wasn't being shown, the update was ignored.
I fixed it by listening to willActivate and didDeactivate to see whether to update the label or whether to save the text so I could apply it when the page is shown.
Apparently this is by design.

IOS Facebook SDK 4.01: Should I be using FBSDKAccessToken.currentAccessToken() to check if user is logged in?

I getting trying to get familiar with FB's newest IOS SDK (4.0.1). I've integrated it into an IOS8 Swift project and using the FBSDKLoginButton to log users in and out. I want the application to skip showing the Login view controller if the user has already logged in.
Should I just be checking the return result of FBSDKAccessToken currentAccessToken()? Does this return 'nil' if a user is not logged in? The docs have the following to say about this method:
You can load this with the SDK from a keychain cache or from an app
bookmark when your app cold launches. You should check its
availability in your view controller's viewDidLoad.
Something like:
// LoginViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if(FBSDKAccessToken.currentAccessToken()){
//They are logged in so show another view
}else{
//They need to log in
}
}
So this sounds like it might be what I'm looking for but I can't really tell from this description. How are the good people of SO handling this common use case? :)
-Nick
Yes, FBSDKAccessToken.currentAccessToken() is what you should be checking although the code you have won't work as this does not return a Boolean. You'll need something like:
if(FBSDKAccessToken.currentAccessToken() != nil) {
//They are logged in so show another view
} else {
//They need to log in
}
Also, this alone only works for when the app moves between foreground/background (user hits home button). If the app is killed and cold launched the token will be lost unless you implement the following in didFinishlaunchingWithOptions in the AppDelegate:
return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
You do not need to worry about the keychain as this is handled for you automatically provided you implement the necessary parts.
The following website has a good tutorial of getting a simple example working: http://www.brianjcoleman.com/tutorial-how-to-use-login-in-facebook-sdk-4-0-for-swift/

Why NSNotification work slow?

I know its interesting title for my question. Sorry for that. Here is my problem;
I am doing simple login screen (with facebook frameworks). If user wants to login with his/her facebook account, firstly he/she must confirm facebook page. And then i get their email addres, name, surname..etc information of user.
After that i just ask them password for my app. This password screen is a view. and opening on main view(not as new viewcontroller. its view on main view) And after confirm facebook page then this wiew appear.
On this password view has 2 textbox and 2 button. textbox for paswword(and confirmation) and First button send request to my web service with user information. and other button is "cancel" button.
here is the problem. When user send request i create nsnotification for waiting respond. after getting respond. i want to this password view get hide.
here is the code:
#IBAction func buttonSave(sender: AnyObject) {
var myObj = connectObject()
myObj.sendRequest("http://localhost:8888/iphone/hearMe/index.php", param: "id=test123")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "actOnSpecialNotification", name: "sendDataCheck", object: nil)
}
func actOnSpecialNotification() {
println("ok I got success respond from webservice")
self.viewPassword.hidden = true /*This is my view */
}
with this code. "viewPassword" view hided but not instantly. It hide after 10 or 15 seconds. How i can hide that view instantly after getting response from webservice?
It's not clear from your question whether the 10-15 seconds is between the pushing of the button and the hiding of the view, or between the printing of "ok I got…" and the hiding of the view.
If the lag is between pushing the button and calling actOnSpecialNotification(), then the above code doesn't help us. We need to know what object posts the notification sendDataCheck and why it takes so long. Maybe it just takes that long to talk to the network. Maybe it posts the notification before you start observing it. You probably want to call addObserver() before sending the request rather than after (just in case there's an async operation in there).
If the lag is between printing the "ok I go…" line and hiding the view, then the most common cause of that is that the notification was posted on a background queue. You can't interact with UIKit anywhere but the main queue, and weird lags are a common symptom when you do.
(Side note: when in doubt, use let, not var. myObj is better defined let here. This helps protect against many kinds of bugs.)