1

I've tried a whole bunch of ways to get Game Center working in my SpriteKit game. Unfortunately the way I've done it in the past using ObjC and ViewControllers don't work because I'm using SKScene/ a GameScene.

This is the swift version of the code (I think):

// MARK: Game Center Integration

//login and make available
func authenticateLocalPlayer(){
    let localPlayer = GKLocalPlayer()
    print(localPlayer)
    localPlayer.authenticateHandler = {(viewController, error) -> Void in
        if ((viewController) != nil) {
            self.presentViewController(viewController!, animated: true, completion: nil)
        }else{
            print((GKLocalPlayer.localPlayer().authenticated))
        }
    }
}

//submit a score to leaderboard
func reportScoreToLeaderboard(thisScore:Int){
    if GKLocalPlayer.localPlayer().authenticated {
        let scoreReporter = GKScore(leaderboardIdentifier: "LeaderboardID")
        scoreReporter.value = Int64(thisScore)
        let scoreArray: [GKScore] = [scoreReporter]

        GKScore.reportScores(scoreArray, withCompletionHandler: { (error: NSError?) -> Void in
            if error != nil {
                print(error!.localizedDescription)
            } else {
                print("Score submitted")
            }
        })
    }
}

//show leaderboard (call from button or touch)
func showLeaderboard() {
    let vc = self.view?.window?.rootViewController
    let gc = GKGameCenterViewController()
    gc.gameCenterDelegate = self
    vc?.presentViewController(gc, animated: true, completion: nil)
} 

//hides view when finished
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController){
    gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}

Unfortunetely, no matter what I try, I either get this error:

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior

... or it just crashes.

I've read that NSNotifications can be used? But how?

I'm guessing the best way is to set it all up in the GameViewController.swift and use NSNotifications to communicate with the RootViewController from the GameScene? I can't seem to find a tutorial or example though.

Fabio Berger
  • 1,908
  • 2
  • 22
  • 28
Reanimation
  • 2,751
  • 10
  • 45
  • 76
  • Well the error is telling you what is wrong, you are presenting a view on a view controller at the time it is deallocating. I would like to point out that this `let vc = self.view?.window?.rootViewController` is bad practice. Here is where your error may lie, since self.view is most likely deallocating at the time you are presenting the gamecenter view controller – Knight0fDragon Dec 21 '15 at 14:37
  • I know it's bad practise, but any resource I find that shows a swift implementation uses it... I'm not sure it's possible to implement GC from inside a GameScene – Reanimation Dec 21 '15 at 15:25
  • sure it is, I already went over delegation in another question you asked, same rule applies with GC – Knight0fDragon Dec 21 '15 at 15:48
  • I think my problem is, my `gameViewController` doesn't have a `viewDidLoad`, it uses `override func viewWillLayoutSubviews()` then the `viewDidLoad` is in the `GameScene: SKScene` class... – Reanimation Dec 21 '15 at 15:51
  • viewDidLoad exists no matter what, you just dont override it with your custom class – Knight0fDragon Dec 21 '15 at 15:52
  • if you did my preferred method, you should have a delegate set up for your AlertView. Well now you add onto this delegate by adding a new protocol for showing GC – Knight0fDragon Dec 21 '15 at 15:53
  • Regarding the viewDidLoad, should I not override viewWillLayout... Or not override the viewDidLoad in the GameScene? – Reanimation Dec 21 '15 at 15:56
  • viewdidload does not exist in gamescene, so I do not know what you are talking about with this – Knight0fDragon Dec 21 '15 at 16:00
  • My mistake, I have viewWillLayoutSubViews() in my GameViewController and it's didMoveToView in my GameScene. Brain fart – Reanimation Dec 21 '15 at 16:04
  • Anyway, you recommend using delegation? I will give it a shot :D – Reanimation Dec 21 '15 at 16:10
  • Yes, because of reasons like this – Knight0fDragon Dec 21 '15 at 16:11
  • So I guess it's just a case of passing the score using the delegation like the alert? But what about showing and closing the leaderboard? – Reanimation Dec 21 '15 at 16:29
  • you would make delegate methods that pass nothing, basically func showLeadershipBoard(). close leadershipboard may get handled by gamecenter, I do not remember off the top of my head (I actually wrote a wrapper class for gamecenter to carry throughout all my projects so it has been a while) if you do it yourself, then it is simply func closeLeadershipBoard() – Knight0fDragon Dec 21 '15 at 16:34
  • It has a dismiss func built in that's called when the done button is pressed, maybe I wouldn't even need to handle it – Reanimation Dec 21 '15 at 16:40
  • not sure, worry about that last I guess – Knight0fDragon Dec 21 '15 at 16:44
  • Yeh (: one step at a time – Reanimation Dec 21 '15 at 16:52
  • 1
    This doesn't answer your core question, but you have a common flaw in `authenticateLocalPlayer()`. You assume that if the viewController is nil, the user is authenticated. However, if the error code is set, the viewController is usually (always?) nil. Thus, an auth error will fall through your code with a null VC and be treated as success. You need to check the value of `error` first. If you don't, it will come back to haunt you eventually. – Thunk Dec 21 '15 at 18:07
  • @Knight0fDragon I've got it all working using the delegation method, it authenticates the player in `viewDidLoad`, I can open the leaderboard from a button/sprite in `GameScene` and I've successfully submitted a highscore from `GameScene` to a reportScore method in the ViewController... but I get this message once I've reported the score: `Received memory warning.` and it follows with this message when I view it again: `This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes` – Reanimation Dec 21 '15 at 22:09
  • Look at this ticket to run code on main thread: http://stackoverflow.com/questions/24985716/in-swift-how-to-call-method-with-parameters-on-gcd-main-thread – Knight0fDragon Dec 21 '15 at 22:13
  • Ah ok, so do I need to place the call from GameScene in `dispatch_async(dispatch_get_main_queue(),` or the function in the ViewController? – Reanimation Dec 21 '15 at 22:17
  • Scrap that, if I go in and out of a Leaderboard 3 times, it still prints both the warnings above... Just when you think you're getting somewhere... – Reanimation Dec 21 '15 at 22:25
  • I've put the functions in the ViewController in the `dispatch_async`. Seems ok, but from what I've read it doesn't always return a warning anyway, so time will tell. Thanks again for your help. It's really appreciated. Fingers crossed no more warnings or memory warnings >. – Reanimation Dec 21 '15 at 22:35
  • Dunno, can't help you any further, you need to step through your code and make sure you understand what is happening with it – Knight0fDragon Dec 22 '15 at 19:45
  • @Knight0fDragon any chance you might post your gamecenter wrapper class on github? :) – Ralph Nov 19 '16 at 07:35

1 Answers1

2

Use delegation when a view controller needs to change views, do not have the view itself change views, this could cause the view trying to deallocating while the new view is being presented, thus the error you are getting

Knight0fDragon
  • 15,949
  • 2
  • 20
  • 41